feat(footer): add customizable footer component with dynamic content and links
This commit is contained in:
parent
b0e3ef461f
commit
294e73e189
8 changed files with 448 additions and 36 deletions
|
|
@ -94,21 +94,17 @@ class ConfigExportCommand extends Command
|
|||
|
||||
/**
|
||||
* Get autocompletion suggestions.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function complete(CompletionInput $input, array $suggestions): array
|
||||
public function complete(CompletionInput $input, \Symfony\Component\Console\Completion\CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestOptionValuesFor('workspace')) {
|
||||
if (class_exists(\Core\Mod\Tenant\Models\Workspace::class)) {
|
||||
return \Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray();
|
||||
$suggestions->suggestValues(\Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('category')) {
|
||||
return \Core\Config\Models\ConfigKey::distinct()->pluck('category')->toArray();
|
||||
$suggestions->suggestValues(\Core\Config\Models\ConfigKey::distinct()->pluck('category')->toArray());
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,17 +172,13 @@ class ConfigImportCommand extends Command
|
|||
|
||||
/**
|
||||
* Get autocompletion suggestions.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function complete(CompletionInput $input, array $suggestions): array
|
||||
public function complete(CompletionInput $input, \Symfony\Component\Console\Completion\CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestOptionValuesFor('workspace')) {
|
||||
if (class_exists(\Core\Mod\Tenant\Models\Workspace::class)) {
|
||||
return \Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray();
|
||||
$suggestions->suggestValues(\Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -400,21 +400,17 @@ class ConfigVersionCommand extends Command
|
|||
|
||||
/**
|
||||
* Get autocompletion suggestions.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function complete(CompletionInput $input, array $suggestions): array
|
||||
public function complete(CompletionInput $input, \Symfony\Component\Console\Completion\CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('action')) {
|
||||
return ['list', 'create', 'show', 'rollback', 'compare', 'diff', 'delete'];
|
||||
$suggestions->suggestValues(['list', 'create', 'show', 'rollback', 'compare', 'diff', 'delete']);
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('workspace')) {
|
||||
if (class_exists(\Core\Mod\Tenant\Models\Workspace::class)) {
|
||||
return \Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray();
|
||||
$suggestions->suggestValues(\Core\Mod\Tenant\Models\Workspace::pluck('slug')->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
{{--
|
||||
Custom Footer Content Partial
|
||||
|
||||
Variables:
|
||||
$customContent - Raw HTML content
|
||||
$customLinks - Array of ['label' => '', 'url' => '', 'icon' => ''] links
|
||||
$socialLinks - Array of ['platform' => '', 'url' => '', 'icon' => ''] social links
|
||||
$contactEmail - Email address
|
||||
$contactPhone - Phone number
|
||||
$showCopyright - Whether to show copyright (for replace mode)
|
||||
$copyrightText - Custom copyright text
|
||||
$workspaceName - Workspace name for default copyright
|
||||
$appName - App name for default copyright
|
||||
$appIcon - App icon path
|
||||
--}}
|
||||
@php
|
||||
$showCopyright = $showCopyright ?? false;
|
||||
$copyrightText = $copyrightText ?? null;
|
||||
$workspaceName = $workspaceName ?? null;
|
||||
$appName = $appName ?? config('core.app.name', 'Core PHP');
|
||||
$appIcon = $appIcon ?? config('core.app.icon', '/images/icon.svg');
|
||||
@endphp
|
||||
|
||||
<div class="max-w-5xl mx-auto px-4 sm:px-6 py-8">
|
||||
{{-- Raw HTML custom content --}}
|
||||
@if(!empty($customContent))
|
||||
<div class="custom-footer-content mb-6">
|
||||
{!! $customContent !!}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Structured content grid --}}
|
||||
@if(!empty($customLinks) || !empty($socialLinks) || $contactEmail || $contactPhone)
|
||||
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 @if(!empty($customContent)) pt-6 border-t border-slate-200 dark:border-slate-700/50 @endif">
|
||||
|
||||
{{-- Contact information --}}
|
||||
@if($contactEmail || $contactPhone)
|
||||
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-4 text-sm text-slate-500">
|
||||
@if($contactEmail)
|
||||
<a href="mailto:{{ $contactEmail }}" class="hover:text-slate-700 dark:hover:text-slate-300 transition flex items-center gap-2">
|
||||
<i class="fa-solid fa-envelope text-xs"></i>
|
||||
{{ $contactEmail }}
|
||||
</a>
|
||||
@endif
|
||||
@if($contactPhone)
|
||||
<a href="tel:{{ preg_replace('/[^0-9+]/', '', $contactPhone) }}" class="hover:text-slate-700 dark:hover:text-slate-300 transition flex items-center gap-2">
|
||||
<i class="fa-solid fa-phone text-xs"></i>
|
||||
{{ $contactPhone }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Custom links --}}
|
||||
@if(!empty($customLinks))
|
||||
<div class="flex flex-wrap items-center gap-4 text-sm text-slate-500">
|
||||
@foreach($customLinks as $link)
|
||||
<a href="{{ $link['url'] }}" class="hover:text-slate-700 dark:hover:text-slate-300 transition flex items-center gap-2">
|
||||
@if(!empty($link['icon']))
|
||||
<i class="{{ $link['icon'] }} text-xs"></i>
|
||||
@endif
|
||||
{{ $link['label'] }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Social links --}}
|
||||
@if(!empty($socialLinks))
|
||||
<div class="flex items-center gap-4">
|
||||
@foreach($socialLinks as $social)
|
||||
@php
|
||||
// Get icon from the social link or generate based on platform
|
||||
$socialIcon = $social['icon'] ?? match(strtolower($social['platform'] ?? '')) {
|
||||
'twitter', 'x' => 'fa-brands fa-x-twitter',
|
||||
'facebook' => 'fa-brands fa-facebook',
|
||||
'instagram' => 'fa-brands fa-instagram',
|
||||
'linkedin' => 'fa-brands fa-linkedin',
|
||||
'youtube' => 'fa-brands fa-youtube',
|
||||
'tiktok' => 'fa-brands fa-tiktok',
|
||||
'github' => 'fa-brands fa-github',
|
||||
'discord' => 'fa-brands fa-discord',
|
||||
'mastodon' => 'fa-brands fa-mastodon',
|
||||
'bluesky' => 'fa-brands fa-bluesky',
|
||||
'threads' => 'fa-brands fa-threads',
|
||||
'pinterest' => 'fa-brands fa-pinterest',
|
||||
default => 'fa-solid fa-link',
|
||||
};
|
||||
@endphp
|
||||
<a
|
||||
href="{{ $social['url'] }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="w-8 h-8 flex items-center justify-center rounded-full text-slate-400 hover:text-slate-600 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition"
|
||||
aria-label="{{ ucfirst($social['platform'] ?? 'Social link') }}"
|
||||
>
|
||||
<i class="{{ $socialIcon }}"></i>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Copyright for replace mode --}}
|
||||
@if($showCopyright)
|
||||
<div class="flex items-center gap-4 mt-6 pt-6 border-t border-slate-200 dark:border-slate-700/50">
|
||||
<img src="{{ $appIcon }}" alt="{{ $appName }}" class="w-6 h-6 opacity-50">
|
||||
<span class="text-sm text-slate-500">
|
||||
{!! $copyrightText ?? '© '.date('Y').' '.e($workspaceName ?? $appName) !!}
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -4,6 +4,18 @@
|
|||
$appUrl = config('app.url', 'https://core.test');
|
||||
$privacyUrl = config('core.urls.privacy', '/privacy');
|
||||
$termsUrl = config('core.urls.terms', '/terms');
|
||||
|
||||
// Footer settings - can be passed as array or FooterSettings object
|
||||
$footer = $footer ?? null;
|
||||
$footerShowDefault = $footer['show_default_links'] ?? $footer?->showDefaultLinks ?? true;
|
||||
$footerPosition = $footer['position'] ?? $footer?->position ?? 'above_default';
|
||||
$footerCustomContent = $footer['custom_content'] ?? $footer?->customContent ?? null;
|
||||
$footerCustomLinks = $footer['custom_links'] ?? $footer?->customLinks ?? [];
|
||||
$footerSocialLinks = $footer['social_links'] ?? $footer?->socialLinks ?? [];
|
||||
$footerCopyright = $footer['copyright_text'] ?? $footer?->copyrightText ?? null;
|
||||
$footerContactEmail = $footer['contact_email'] ?? $footer?->contactEmail ?? null;
|
||||
$footerContactPhone = $footer['contact_phone'] ?? $footer?->contactPhone ?? null;
|
||||
$footerHasCustom = $footerCustomContent || !empty($footerCustomLinks) || !empty($footerSocialLinks) || $footerContactEmail || $footerContactPhone;
|
||||
@endphp
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="scroll-smooth overscroll-none"
|
||||
|
|
@ -145,12 +157,42 @@
|
|||
<footer class="mt-auto">
|
||||
<!-- Footer gradient border -->
|
||||
<div class="h-px w-full bg-gradient-to-r from-transparent via-violet-500/20 to-transparent"></div>
|
||||
|
||||
{{-- Custom footer content (above default) --}}
|
||||
@if($footerHasCustom && $footerPosition === 'above_default')
|
||||
@include('front::components.satellite.footer-custom', [
|
||||
'customContent' => $footerCustomContent,
|
||||
'customLinks' => $footerCustomLinks,
|
||||
'socialLinks' => $footerSocialLinks,
|
||||
'contactEmail' => $footerContactEmail,
|
||||
'contactPhone' => $footerContactPhone,
|
||||
])
|
||||
@endif
|
||||
|
||||
{{-- Custom footer content (replace default) --}}
|
||||
@if($footerHasCustom && $footerPosition === 'replace_default')
|
||||
@include('front::components.satellite.footer-custom', [
|
||||
'customContent' => $footerCustomContent,
|
||||
'customLinks' => $footerCustomLinks,
|
||||
'socialLinks' => $footerSocialLinks,
|
||||
'contactEmail' => $footerContactEmail,
|
||||
'contactPhone' => $footerContactPhone,
|
||||
'showCopyright' => true,
|
||||
'copyrightText' => $footerCopyright,
|
||||
'workspaceName' => $workspace?->name,
|
||||
'appName' => $appName,
|
||||
'appIcon' => $appIcon,
|
||||
])
|
||||
@endif
|
||||
|
||||
{{-- Default footer --}}
|
||||
@if($footerShowDefault && $footerPosition !== 'replace_default')
|
||||
<div class="max-w-5xl mx-auto px-4 sm:px-6 py-8">
|
||||
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="{{ $appIcon }}" alt="{{ $appName }}" class="w-6 h-6 opacity-50">
|
||||
<span class="text-sm text-slate-500">
|
||||
© {{ date('Y') }} {{ $workspace?->name ?? $appName }}
|
||||
{!! $footerCopyright ?? '© '.date('Y').' '.e($workspace?->name ?? $appName) !!}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-6 text-sm text-slate-500">
|
||||
|
|
@ -163,6 +205,18 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Custom footer content (below default) --}}
|
||||
@if($footerHasCustom && $footerPosition === 'below_default')
|
||||
@include('front::components.satellite.footer-custom', [
|
||||
'customContent' => $footerCustomContent,
|
||||
'customLinks' => $footerCustomLinks,
|
||||
'socialLinks' => $footerSocialLinks,
|
||||
'contactEmail' => $footerContactEmail,
|
||||
'contactPhone' => $footerContactPhone,
|
||||
])
|
||||
@endif
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Website\Service\View\Features;
|
||||
use Core\Website\Service\View\Landing;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
|
@ -16,3 +17,4 @@ use Illuminate\Support\Facades\Route;
|
|||
*/
|
||||
|
||||
Route::get('/', Landing::class)->name('service.home');
|
||||
Route::get('/features', Features::class)->name('service.features');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
<div>
|
||||
{{-- Hero Section --}}
|
||||
<section class="relative overflow-hidden">
|
||||
<div class="relative max-w-7xl mx-auto px-6 md:px-10 xl:px-8 py-20 lg:py-28">
|
||||
<div class="text-center max-w-3xl mx-auto">
|
||||
{{-- Badge --}}
|
||||
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full text-sm mb-6" style="background-color: color-mix(in srgb, var(--service-accent) 15%, transparent); border: 1px solid color-mix(in srgb, var(--service-accent) 30%, transparent); color: var(--service-accent);">
|
||||
<i class="fa-solid fa-{{ $workspace['icon'] ?? 'cube' }}"></i>
|
||||
{{ $workspace['name'] ?? 'Service' }} Features
|
||||
</div>
|
||||
|
||||
{{-- Headline --}}
|
||||
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-white mb-6 leading-tight">
|
||||
Everything you need to
|
||||
<span style="color: var(--service-accent);">succeed</span>
|
||||
</h1>
|
||||
|
||||
{{-- Description --}}
|
||||
<p class="text-lg text-slate-400 mb-8 max-w-xl mx-auto">
|
||||
{{ $workspace['description'] ?? 'Powerful features built for creators and businesses.' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- Features Grid --}}
|
||||
<section class="py-20 lg:py-28 bg-slate-900/50">
|
||||
<div class="max-w-7xl mx-auto px-6 md:px-10 xl:px-8">
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
@foreach($features as $feature)
|
||||
<div class="rounded-2xl p-8 transition group" style="background-color: color-mix(in srgb, var(--service-accent) 5%, rgb(30 41 59)); border: 1px solid color-mix(in srgb, var(--service-accent) 15%, transparent);">
|
||||
<div class="w-14 h-14 rounded-xl flex items-center justify-center mb-6 transition" style="background-color: color-mix(in srgb, var(--service-accent) 15%, transparent);">
|
||||
<i class="fa-solid fa-{{ $feature['icon'] }} text-xl" style="color: var(--service-accent);"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-white mb-3">{{ $feature['title'] }}</h3>
|
||||
<p class="text-slate-400 leading-relaxed">{{ $feature['description'] }}</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- Integration Section --}}
|
||||
<section class="py-20 lg:py-28">
|
||||
<div class="max-w-4xl mx-auto px-6 md:px-10 xl:px-8 text-center">
|
||||
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4">
|
||||
Part of the Host UK ecosystem
|
||||
</h2>
|
||||
<p class="text-lg text-slate-400 mb-10 max-w-2xl mx-auto">
|
||||
{{ $workspace['name'] ?? 'This service' }} works seamlessly with other Host UK services.
|
||||
One account, unified billing, integrated tools.
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href="https://{{ app()->environment('local') ? 'social.host.test' : 'social.host.uk.com' }}" class="px-4 py-2 rounded-lg bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition">
|
||||
<i class="fa-solid fa-share-nodes mr-2"></i>SocialHost
|
||||
</a>
|
||||
<a href="https://{{ app()->environment('local') ? 'analytics.host.test' : 'analytics.host.uk.com' }}" class="px-4 py-2 rounded-lg bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition">
|
||||
<i class="fa-solid fa-chart-line mr-2"></i>AnalyticsHost
|
||||
</a>
|
||||
<a href="https://{{ app()->environment('local') ? 'notify.host.test' : 'notify.host.uk.com' }}" class="px-4 py-2 rounded-lg bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition">
|
||||
<i class="fa-solid fa-bell mr-2"></i>NotifyHost
|
||||
</a>
|
||||
<a href="https://{{ app()->environment('local') ? 'trust.host.test' : 'trust.host.uk.com' }}" class="px-4 py-2 rounded-lg bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition">
|
||||
<i class="fa-solid fa-star mr-2"></i>TrustHost
|
||||
</a>
|
||||
<a href="https://{{ app()->environment('local') ? 'support.host.test' : 'support.host.uk.com' }}" class="px-4 py-2 rounded-lg bg-slate-800 text-slate-300 text-sm hover:bg-slate-700 transition">
|
||||
<i class="fa-solid fa-headset mr-2"></i>SupportHost
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- CTA Section --}}
|
||||
<section class="py-20" style="background: linear-gradient(135deg, var(--service-accent), color-mix(in srgb, var(--service-accent) 70%, black));">
|
||||
<div class="max-w-4xl mx-auto px-6 md:px-10 xl:px-8 text-center">
|
||||
<h2 class="text-3xl sm:text-4xl font-bold mb-4 text-slate-900">
|
||||
Ready to get started?
|
||||
</h2>
|
||||
<p class="text-lg mb-8 text-slate-800">
|
||||
Start your free trial today. No credit card required.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<a href="{{ config('app.url') }}/register" class="inline-flex items-center justify-center px-8 py-3 font-semibold rounded-xl transition shadow-lg bg-slate-900" style="color: var(--service-accent);">
|
||||
Get started free
|
||||
</a>
|
||||
<a href="/pricing" class="inline-flex items-center justify-center px-8 py-3 font-semibold rounded-xl transition bg-white/20 text-slate-900 border border-slate-900/20 hover:bg-white/30">
|
||||
View pricing
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
162
packages/core-php/src/Website/Service/View/Features.php
Normal file
162
packages/core-php/src/Website/Service/View/Features.php
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
/*
|
||||
* Core PHP Framework
|
||||
*
|
||||
* Licensed under the European Union Public Licence (EUPL) v1.2.
|
||||
* See LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Website\Service\View;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Generic Service Features Page.
|
||||
*
|
||||
* Displays service features with dynamic theming.
|
||||
*/
|
||||
class Features extends Component
|
||||
{
|
||||
public array $workspace = [];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
// Extract subdomain from host (e.g., social.host.test → social)
|
||||
$host = request()->getHost();
|
||||
$slug = $this->extractSubdomain($host);
|
||||
|
||||
// Try to resolve workspace from app container if service exists
|
||||
if ($slug && app()->bound('workspace.service')) {
|
||||
$this->workspace = app('workspace.service')->get($slug) ?? [];
|
||||
}
|
||||
|
||||
// Fallback to app config defaults
|
||||
if (empty($this->workspace)) {
|
||||
$this->workspace = [
|
||||
'name' => config('core.app.name', config('app.name', 'Service')),
|
||||
'slug' => 'service',
|
||||
'icon' => config('core.app.icon', 'cube'),
|
||||
'color' => config('core.app.color', 'violet'),
|
||||
'description' => config('core.app.description', 'A powerful platform'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract subdomain from hostname.
|
||||
*/
|
||||
protected function extractSubdomain(string $host): ?string
|
||||
{
|
||||
if (preg_match('/^([a-z]+)\.host\.(test|localhost|uk\.com)$/i', $host, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed features for this service.
|
||||
*/
|
||||
public function getFeatures(): array
|
||||
{
|
||||
$slug = $this->workspace['slug'] ?? 'service';
|
||||
|
||||
// Service-specific features
|
||||
return match ($slug) {
|
||||
'social' => $this->getSocialFeatures(),
|
||||
'analytics' => $this->getAnalyticsFeatures(),
|
||||
'notify' => $this->getNotifyFeatures(),
|
||||
'trust' => $this->getTrustFeatures(),
|
||||
'support' => $this->getSupportFeatures(),
|
||||
default => $this->getDefaultFeatures(),
|
||||
};
|
||||
}
|
||||
|
||||
protected function getSocialFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'calendar', 'title' => 'Schedule posts', 'description' => 'Plan your content calendar weeks in advance with our visual scheduler.'],
|
||||
['icon' => 'share-nodes', 'title' => 'Multi-platform publishing', 'description' => 'Publish to 20+ social networks from a single dashboard.'],
|
||||
['icon' => 'chart-pie', 'title' => 'Analytics & insights', 'description' => 'Track engagement, reach, and growth across all your accounts.'],
|
||||
['icon' => 'users', 'title' => 'Team collaboration', 'description' => 'Work together with approval workflows and role-based access.'],
|
||||
['icon' => 'wand-magic-sparkles', 'title' => 'AI content assistant', 'description' => 'Generate captions, hashtags, and post ideas with AI.'],
|
||||
['icon' => 'inbox', 'title' => 'Unified inbox', 'description' => 'Manage comments and messages from all platforms in one place.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getAnalyticsFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'cookie-bite', 'title' => 'No cookies required', 'description' => 'Privacy-focused tracking without consent banners.'],
|
||||
['icon' => 'bolt', 'title' => 'Lightweight script', 'description' => 'Under 1KB script that won\'t slow down your site.'],
|
||||
['icon' => 'chart-line', 'title' => 'Real-time dashboard', 'description' => 'See visitors on your site as they browse.'],
|
||||
['icon' => 'route', 'title' => 'Goal tracking', 'description' => 'Set up funnels and conversion goals to measure success.'],
|
||||
['icon' => 'flask', 'title' => 'A/B testing', 'description' => 'Test variations and measure statistical significance.'],
|
||||
['icon' => 'file-export', 'title' => 'Data export', 'description' => 'Export your data anytime in CSV or JSON format.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getNotifyFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'bell', 'title' => 'Web push notifications', 'description' => 'Reach subscribers directly in their browser.'],
|
||||
['icon' => 'users-gear', 'title' => 'Audience segments', 'description' => 'Target specific groups based on behaviour and preferences.'],
|
||||
['icon' => 'diagram-project', 'title' => 'Automation flows', 'description' => 'Create drip campaigns triggered by user actions.'],
|
||||
['icon' => 'vials', 'title' => 'A/B testing', 'description' => 'Test different messages to optimise engagement.'],
|
||||
['icon' => 'chart-simple', 'title' => 'Delivery analytics', 'description' => 'Track delivery, clicks, and conversion rates.'],
|
||||
['icon' => 'clock', 'title' => 'Scheduled sends', 'description' => 'Schedule notifications for optimal delivery times.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getTrustFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'comment-dots', 'title' => 'Social proof popups', 'description' => 'Show real-time purchase and signup notifications.'],
|
||||
['icon' => 'star', 'title' => 'Review collection', 'description' => 'Collect and display customer reviews automatically.'],
|
||||
['icon' => 'bullseye', 'title' => 'Smart targeting', 'description' => 'Show notifications to the right visitors at the right time.'],
|
||||
['icon' => 'palette', 'title' => 'Custom styling', 'description' => 'Match notifications to your brand with full CSS control.'],
|
||||
['icon' => 'chart-column', 'title' => 'Conversion tracking', 'description' => 'Measure the impact on your conversion rates.'],
|
||||
['icon' => 'plug', 'title' => 'Easy integration', 'description' => 'Add to any website with a single script tag.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getSupportFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'inbox', 'title' => 'Shared inbox', 'description' => 'Manage customer emails from a unified team inbox.'],
|
||||
['icon' => 'book', 'title' => 'Help centre', 'description' => 'Build a self-service knowledge base for customers.'],
|
||||
['icon' => 'comments', 'title' => 'Live chat widget', 'description' => 'Embed a chat widget on your website for instant support.'],
|
||||
['icon' => 'clock-rotate-left', 'title' => 'SLA tracking', 'description' => 'Set response time targets and track performance.'],
|
||||
['icon' => 'reply', 'title' => 'Canned responses', 'description' => 'Save time with pre-written replies for common questions.'],
|
||||
['icon' => 'tags', 'title' => 'Ticket management', 'description' => 'Organise conversations with tags and custom fields.'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDefaultFeatures(): array
|
||||
{
|
||||
return [
|
||||
['icon' => 'rocket', 'title' => 'Easy to use', 'description' => 'Get started in minutes with our intuitive interface.'],
|
||||
['icon' => 'shield-check', 'title' => 'Secure by default', 'description' => 'Built with security and privacy at the core.'],
|
||||
['icon' => 'chart-line', 'title' => 'Analytics included', 'description' => 'Track performance with built-in analytics.'],
|
||||
['icon' => 'puzzle-piece', 'title' => 'Modular architecture', 'description' => 'Extend with modules to fit your exact needs.'],
|
||||
['icon' => 'headset', 'title' => 'UK-based support', 'description' => 'Get help from our friendly support team.'],
|
||||
['icon' => 'code', 'title' => 'Developer friendly', 'description' => 'Full API access and webhook integrations.'],
|
||||
];
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$appName = config('core.app.name', config('app.name', 'Service'));
|
||||
|
||||
return view('service::features', [
|
||||
'workspace' => $this->workspace,
|
||||
'features' => $this->getFeatures(),
|
||||
])->layout('service::layouts.service', [
|
||||
'title' => 'Features - ' . ($this->workspace['name'] ?? $appName),
|
||||
'workspace' => $this->workspace,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue