From 4988cb85bd2f1de52da2bf2fef670fb100795af3 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Fri, 24 Jul 2026 15:16:10 +0100 Subject: [PATCH] breakign changes --- .../Resources/views/public/index.blade.php | 28 +++- .../Resources/views/public/show.blade.php | 20 ++- .../Resources/views/public/index.blade.php | 28 +++- .../Resources/views/public/show.blade.php | 20 ++- .../Resources/views/public/show.blade.php | 19 ++- .../Providers/StylistServiceProvider.php | 21 +++ .../Stylist/Support/FrontendNavigation.php | 121 ++++++++++++++++++ resources/themes/saasland/css/theme.css | 4 + resources/themes/saasland/theme.json | 2 +- .../themes/saasland/views/home.blade.php | 82 ++---------- .../saasland/views/layouts/app.blade.php | 62 +++++++++ .../saasland/views/partials/footer.blade.php | 36 ++++++ .../saasland/views/partials/header.blade.php | 68 ++++++++++ .../views/partials/navigation-items.blade.php | 20 +++ routes/web.php | 2 +- 15 files changed, 457 insertions(+), 76 deletions(-) create mode 100644 Modules/Stylist/Support/FrontendNavigation.php create mode 100644 resources/themes/saasland/views/layouts/app.blade.php create mode 100644 resources/themes/saasland/views/partials/footer.blade.php create mode 100644 resources/themes/saasland/views/partials/header.blade.php create mode 100644 resources/themes/saasland/views/partials/navigation-items.blade.php diff --git a/Modules/Blog/Resources/views/public/index.blade.php b/Modules/Blog/Resources/views/public/index.blade.php index 64a2e23..f5c81f0 100644 --- a/Modules/Blog/Resources/views/public/index.blade.php +++ b/Modules/Blog/Resources/views/public/index.blade.php @@ -1 +1,27 @@ -

Blog

@foreach($records as $record)@endforeach
{{ $records->links() }}
+ +
+
+
+ @forelse($records as $record) +
+
+
+ +

{{ $record->title }}

+ @if($record->excerpt)

{{ $record->excerpt }}

@endif + Read article → +
+
+ @empty +
No blog posts have been published yet.
+ @endforelse +
+
{{ $records->links() }}
+
+
+
diff --git a/Modules/Blog/Resources/views/public/show.blade.php b/Modules/Blog/Resources/views/public/show.blade.php index 882c377..878edb7 100644 --- a/Modules/Blog/Resources/views/public/show.blade.php +++ b/Modules/Blog/Resources/views/public/show.blade.php @@ -1 +1,19 @@ -

{{ $record->title }}

{{ optional($record->published_at)->format('j F Y') }}

{!! nl2br(e($record->content)) !!}
+ +
+
+
+ + @if($record->excerpt)

{{ $record->excerpt }}

@endif +
{!! nl2br(e($record->content)) !!}
+
+
+
+
diff --git a/Modules/News/Resources/views/public/index.blade.php b/Modules/News/Resources/views/public/index.blade.php index e0a05ca..f78bc96 100644 --- a/Modules/News/Resources/views/public/index.blade.php +++ b/Modules/News/Resources/views/public/index.blade.php @@ -1 +1,27 @@ -

News

@foreach($records as $record)@endforeach
{{ $records->links() }}
+ +
+
+
+ @forelse($records as $record) +
+
+
+ +

{{ $record->title }}

+ @if($record->excerpt)

{{ $record->excerpt }}

@endif + Read update → +
+
+ @empty +
No news has been published yet.
+ @endforelse +
+
{{ $records->links() }}
+
+
+
diff --git a/Modules/News/Resources/views/public/show.blade.php b/Modules/News/Resources/views/public/show.blade.php index 882c377..ebea25f 100644 --- a/Modules/News/Resources/views/public/show.blade.php +++ b/Modules/News/Resources/views/public/show.blade.php @@ -1 +1,19 @@ -

{{ $record->title }}

{{ optional($record->published_at)->format('j F Y') }}

{!! nl2br(e($record->content)) !!}
+ +
+
+
+ + @if($record->excerpt)

{{ $record->excerpt }}

@endif +
{!! nl2br(e($record->content)) !!}
+
+
+
+
diff --git a/Modules/Pages/Resources/views/public/show.blade.php b/Modules/Pages/Resources/views/public/show.blade.php index ee80ff9..9f3f52f 100644 --- a/Modules/Pages/Resources/views/public/show.blade.php +++ b/Modules/Pages/Resources/views/public/show.blade.php @@ -1 +1,18 @@ -

{{ $page->title }}

@if($page->summary)

{{ $page->summary }}

@endif{!! $page->content !!}
\ No newline at end of file + +
+
+
+ @if ($page->summary) +

{{ $page->summary }}

+ @endif + +
{!! $page->content !!}
+
+
+
+
diff --git a/Modules/Stylist/Providers/StylistServiceProvider.php b/Modules/Stylist/Providers/StylistServiceProvider.php index c4ccfea..b0bc209 100644 --- a/Modules/Stylist/Providers/StylistServiceProvider.php +++ b/Modules/Stylist/Providers/StylistServiceProvider.php @@ -13,7 +13,10 @@ use Modules\Stylist\Console\ThemeDoctorCommand; use Modules\Stylist\Console\ValidateThemeCommand; use Modules\Stylist\Html\ThemeHtmlBuilder; use Modules\Stylist\Html\ThemeSeoBuilder; +use Modules\Stylist\Support\FrontendNavigation; use Modules\Stylist\Support\ModuleInfo; +use Modules\Stylist\Services\ThemeSettings; +use Modules\Stylist\Theme\Loader; use Modules\Stylist\Theme\Stylist; final class StylistServiceProvider extends ServiceProvider @@ -62,6 +65,24 @@ final class StylistServiceProvider extends ServiceProvider $view->with('activeTheme', $this->app->make(Stylist::class)->current()); }); + + + View::composer('stylist-theme-saasland::*', function ($view): void { + $theme = $this->app->make(Loader::class)->find('saasland'); + $settings = $this->app->make(ThemeSettings::class)->get($theme); + $navigation = $this->app->make(FrontendNavigation::class); + + $view->with([ + 'siteTitle' => filled($settings['site_title'] ?? null) ? $settings['site_title'] : config('app.name', 'Saasland'), + 'siteDescription' => filled($settings['site_description'] ?? null) ? $settings['site_description'] : 'Modern software for modern companies.', + 'logoUrl' => $settings['logo_url'] ?? null, + 'facebookUrl' => $settings['facebook_url'] ?? null, + 'xUrl' => $settings['x_url'] ?? null, + 'linkedinUrl' => $settings['linkedin_url'] ?? null, + 'primaryNavigation' => $navigation->primary(), + 'footerNavigation' => $navigation->footer(), + ]); + }); } private function registerViews(): void diff --git a/Modules/Stylist/Support/FrontendNavigation.php b/Modules/Stylist/Support/FrontendNavigation.php new file mode 100644 index 0000000..8bfc071 --- /dev/null +++ b/Modules/Stylist/Support/FrontendNavigation.php @@ -0,0 +1,121 @@ +> */ + public function primary(): array + { + return $this->forLocations(['primary', 'header', 'main'], $this->fallbackPrimary()); + } + + /** @return array> */ + public function footer(): array + { + return $this->forLocations(['footer'], $this->fallbackFooter()); + } + + /** @param array $locations + * @param array> $fallback + * @return array> + */ + private function forLocations(array $locations, array $fallback): array + { + try { + if (! Schema::hasTable('menus')) { + return $fallback; + } + + $menu = Menu::query() + ->where('enabled', true) + ->whereIn('location', $locations) + ->orderByRaw('CASE location '.collect($locations)->map(fn (string $location, int $index) => "WHEN ? THEN {$index}")->implode(' ').' ELSE 999 END', $locations) + ->first(); + + if (! $menu || ! is_array($menu->items) || $menu->items === []) { + return $fallback; + } + + $items = $this->normaliseItems($menu->items); + + return $items === [] ? $fallback : $items; + } catch (Throwable) { + return $fallback; + } + } + + /** @param array $items + * @return array> + */ + private function normaliseItems(array $items): array + { + return collect($items) + ->filter(fn (mixed $item): bool => is_array($item) && ($item['enabled'] ?? true) !== false) + ->map(function (array $item): ?array { + $label = trim((string) ($item['label'] ?? $item['title'] ?? $item['name'] ?? '')); + if ($label === '') { + return null; + } + + $url = $this->resolveUrl($item); + if ($url === null) { + return null; + } + + return [ + 'label' => $label, + 'url' => $url, + 'target' => ($item['target'] ?? null) === '_blank' ? '_blank' : null, + 'children' => $this->normaliseItems((array) ($item['children'] ?? $item['items'] ?? [])), + ]; + }) + ->filter() + ->values() + ->all(); + } + + /** @param array $item */ + private function resolveUrl(array $item): ?string + { + $routeName = trim((string) ($item['route'] ?? $item['route_name'] ?? '')); + if ($routeName !== '' && Route::has($routeName)) { + return route($routeName, (array) ($item['parameters'] ?? [])); + } + + $pageSlug = trim((string) ($item['page_slug'] ?? $item['slug'] ?? '')); + if ($pageSlug !== '' && Route::has('pages.public.show')) { + return route('pages.public.show', $pageSlug); + } + + $url = trim((string) ($item['url'] ?? $item['href'] ?? '')); + + return $url !== '' ? $url : null; + } + + /** @return array> */ + private function fallbackPrimary(): array + { + $items = [['label' => 'Home', 'url' => url('/'), 'target' => null, 'children' => []]]; + + if (Route::has('blog.public.index')) { + $items[] = ['label' => 'Blog', 'url' => route('blog.public.index'), 'target' => null, 'children' => []]; + } + if (Route::has('news.public.index')) { + $items[] = ['label' => 'News', 'url' => route('news.public.index'), 'target' => null, 'children' => []]; + } + + return $items; + } + + /** @return array> */ + private function fallbackFooter(): array + { + return $this->fallbackPrimary(); + } +} diff --git a/resources/themes/saasland/css/theme.css b/resources/themes/saasland/css/theme.css index 17b810c..b7b9666 100644 --- a/resources/themes/saasland/css/theme.css +++ b/resources/themes/saasland/css/theme.css @@ -2,3 +2,7 @@ /* Privacy and terms homepage areas */ .legal-area{scroll-margin-top:72px}.legal-grid{align-items:stretch}.legal-card{display:flex;flex-direction:column}.legal-card h3{margin-bottom:10px}.legal-card p{margin:0;color:var(--muted)}.legal-note{margin:28px auto 0;max-width:820px;padding:22px 24px;border:1px solid #7c3d18;border-radius:14px;background:#f973160b;text-align:center}.legal-note strong{display:block;color:var(--orange2);margin-bottom:5px}.legal-note p{margin:0;color:var(--muted)}.legal-note a{color:var(--orange2);text-decoration:underline;text-underline-offset:3px} + +/* Reusable public frontend shell */ +.logo img{display:block;max-height:38px;max-width:190px}.desktop-nav{align-items:center}.nav-actions{display:flex;align-items:center;gap:12px}.nav-login,.nav-account{color:#d8d1ca;font-weight:700;font-size:14px}.nav-login:hover,.nav-account:hover{color:var(--orange)}.nav-register{padding:9px 16px;font-size:14px}.menu{width:42px;height:42px;border:1px solid var(--line);border-radius:10px;align-items:center;justify-content:center;flex-direction:column;gap:5px}.menu span{display:block;width:19px;height:2px;background:#fff;border-radius:99px}.nav-dropdown{position:relative}.nav-dropdown-toggle{display:flex;align-items:center;gap:5px;padding:0;border:0;background:none;color:#c8c2bb;font:inherit;cursor:pointer}.nav-dropdown-toggle:hover{color:var(--orange)}.nav-dropdown-menu,.account-dropdown{position:absolute;top:calc(100% + 14px);min-width:190px;padding:9px;border:1px solid var(--line);border-radius:12px;background:#151310;box-shadow:0 20px 55px #0009}.nav-dropdown-menu a,.account-dropdown a,.account-dropdown button{display:block;width:100%;padding:10px 12px;border-radius:8px;color:#d8d1ca;text-align:left;font:inherit}.nav-dropdown-menu a:hover,.account-dropdown a:hover,.account-dropdown button:hover{background:#f9731615;color:var(--orange)}.account-menu{position:relative}.account-trigger{display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:999px;padding:5px 11px 5px 5px;background:#151310;color:#fff;cursor:pointer}.account-trigger>span:first-child{display:grid;place-items:center;width:30px;height:30px;border-radius:50%;background:linear-gradient(135deg,var(--orange),#c2410c);font-weight:850}.account-name{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:700}.account-dropdown{right:0}.account-dropdown form,.account-dropdown button{margin:0;border:0;background:none}.mobile-auth{display:flex;flex-direction:column;gap:4px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}.mobile-auth button{width:100%;border:0;background:none;color:#d8d1ca;text-align:left;font:inherit;padding:10px}.mobile-primary{color:var(--orange)!important;font-weight:800}.page-main{min-height:45vh;background:linear-gradient(180deg,#0c0b0a,#12100e 45%,#0c0b0a)}.page-hero{position:relative;overflow:hidden;padding:92px 0 78px;border-bottom:1px solid var(--line);background:radial-gradient(circle at 30% 0,#3a210f 0,transparent 40%),linear-gradient(145deg,#0c0b0a,#171108)}.page-orb{position:absolute;border-radius:50%;filter:blur(80px);opacity:.13;background:var(--orange)}.page-orb-one{width:360px;height:360px;right:10%;top:-50%}.page-orb-two{width:260px;height:260px;left:5%;bottom:-70%}.page-hero-inner{position:relative}.page-hero h1{max-width:900px;margin:16px 0 12px;font-size:clamp(42px,6vw,70px);line-height:1.02;letter-spacing:-.05em}.page-hero p{max-width:720px;margin:0;color:#bbb3ab;font-size:18px}.breadcrumbs{display:flex;align-items:center;gap:9px;margin-bottom:20px;color:#8f877f;font-size:13px}.breadcrumbs a:hover{color:var(--orange)}.content-section{padding:76px 0 96px}.content-layout{max-width:940px}.content-card{position:relative;padding:clamp(28px,5vw,64px);overflow:hidden;border:1px solid var(--line);border-radius:20px;background:linear-gradient(145deg,#191714,#11100e);box-shadow:0 30px 80px #0004}.content-card:before{content:"";position:absolute;inset:0 auto auto 0;width:100%;height:4px;background:linear-gradient(90deg,var(--orange),#fbbf24,transparent)}.content-lead{margin:0 0 32px;padding-bottom:28px;border-bottom:1px solid var(--line);color:#ddd5cd;font-size:clamp(19px,2.5vw,24px);line-height:1.55}.cms-content{color:#c4bdb5;font-size:17px}.cms-content h2,.cms-content h3,.cms-content h4{color:#fff;line-height:1.2;letter-spacing:-.025em}.cms-content h2{margin:2em 0 .65em;font-size:32px}.cms-content h3{margin:1.7em 0 .55em;font-size:24px}.cms-content p,.cms-content ul,.cms-content ol,.cms-content blockquote{margin:0 0 1.35em}.cms-content a{color:var(--orange2);text-decoration:underline;text-underline-offset:3px}.cms-content img{max-width:100%;height:auto;border-radius:14px}.cms-content blockquote{padding:18px 22px;border-left:3px solid var(--orange);border-radius:0 10px 10px 0;background:#f973160b;color:#e9e2da}.cms-content table{width:100%;border-collapse:collapse;margin:28px 0}.cms-content th,.cms-content td{padding:12px;border:1px solid var(--line);text-align:left}.cms-content th{background:#ffffff08;color:#fff}.article-meta{display:flex;flex-wrap:wrap;gap:10px 20px;margin-bottom:24px;color:#928a82;font-size:14px}.article-meta span+time:before{content:"•";margin-right:20px;color:var(--orange)}.content-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px}.post-card{position:relative;overflow:hidden;border:1px solid var(--line);border-radius:18px;background:linear-gradient(145deg,#191714,#11100e);transition:transform .25s,border-color .25s,box-shadow .25s}.post-card:hover{transform:translateY(-5px);border-color:#7c3d18;box-shadow:0 24px 60px #0005}.post-card-accent{height:5px;background:linear-gradient(90deg,var(--orange),#fbbf24)}.post-card:nth-child(3n+2) .post-card-accent{background:linear-gradient(90deg,#8b5cf6,#ec4899)}.post-card:nth-child(3n+3) .post-card-accent{background:linear-gradient(90deg,#14b8a6,#22c55e)}.post-card-body{padding:28px}.post-meta{color:var(--orange2);font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.08em}.post-card h2{margin:12px 0;font-size:25px;line-height:1.18}.post-card h2 a:hover{color:var(--orange)}.post-card p{color:var(--muted)}.read-more{display:inline-block;margin-top:8px;color:var(--orange2);font-weight:800}.empty-state{grid-column:1/-1;padding:54px;border:1px dashed #4a423a;border-radius:16px;color:var(--muted);text-align:center}.theme-pagination{margin-top:38px}.theme-pagination nav{color:#c8c2bb}.theme-pagination a,.theme-pagination span{border-color:var(--line)!important;background:#151310!important;color:#c8c2bb!important}.theme-pagination span[aria-current="page"] span{background:var(--orange)!important;color:#fff!important}.copyright{display:flex;justify-content:space-between;gap:20px}.footer-brand{max-width:340px} +@media(max-width:980px){.desktop-nav,.nav-login,.nav-account,.nav-register,.account-menu{display:none}.menu{display:flex}.mobile-nav{top:72px;max-height:calc(100vh - 72px);overflow:auto;border-bottom:1px solid var(--line)}.mobile-nav>a,.mobile-nav>.nav-dropdown>a,.mobile-nav .nav-dropdown-toggle{padding:10px;color:#d8d1ca}.mobile-nav .nav-dropdown{display:flex;flex-direction:column}.mobile-nav .nav-dropdown-menu{position:static;margin:0 0 8px 12px;box-shadow:none}.content-grid{grid-template-columns:1fr}}@media(max-width:640px){.page-hero{padding:68px 0 56px}.content-section{padding:48px 0 70px}.content-card{border-radius:15px}.article-meta span+time:before{display:none}.copyright{flex-direction:column}.account-name{display:none}} diff --git a/resources/themes/saasland/theme.json b/resources/themes/saasland/theme.json index fc13faa..f011506 100644 --- a/resources/themes/saasland/theme.json +++ b/resources/themes/saasland/theme.json @@ -16,7 +16,7 @@ "animations": true, "privacy": true, "terms": true, - "auth": false, + "auth": true, "preview": true, "variants": true, "settings": true, diff --git a/resources/themes/saasland/views/home.blade.php b/resources/themes/saasland/views/home.blade.php index f4add68..7e053b2 100644 --- a/resources/themes/saasland/views/home.blade.php +++ b/resources/themes/saasland/views/home.blade.php @@ -1,41 +1,8 @@ -@push('theme-head') - -@endpush - -
- - - - -
+
@@ -409,36 +376,13 @@

Ready to transform your business?

Start building a faster, more connected operation today.

- Get started free → + @auth + Open dashboard → + @else + @if (Route::has('register')) + Get started free → + @endif + @endauth
-
- - -
+ diff --git a/resources/themes/saasland/views/layouts/app.blade.php b/resources/themes/saasland/views/layouts/app.blade.php new file mode 100644 index 0000000..c58a572 --- /dev/null +++ b/resources/themes/saasland/views/layouts/app.blade.php @@ -0,0 +1,62 @@ +@props([ + 'title' => null, + 'description' => null, + 'eyebrow' => null, + 'hero' => false, + 'breadcrumbs' => [], +]) + + + + + + + + + {{ $title && $title !== $siteTitle ? $title.' — '.$siteTitle : $siteTitle }} + + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + @stack('theme-head') + + +
+ @include('stylist-theme-saasland::partials.header') + + @unless($hero) +
+
+
+
+ @if ($breadcrumbs) + + @endif + @if ($eyebrow){{ $eyebrow }}@endif +

{{ $title }}

+ @if ($description)

{{ $description }}

@endif +
+
+ @endunless + +
+ {{ $slot }} +
+ + @include('stylist-theme-saasland::partials.footer') +
+ + +@stack('theme-scripts') + + diff --git a/resources/themes/saasland/views/partials/footer.blade.php b/resources/themes/saasland/views/partials/footer.blade.php new file mode 100644 index 0000000..3d95dcc --- /dev/null +++ b/resources/themes/saasland/views/partials/footer.blade.php @@ -0,0 +1,36 @@ +
+ + + +
diff --git a/resources/themes/saasland/views/partials/header.blade.php b/resources/themes/saasland/views/partials/header.blade.php new file mode 100644 index 0000000..29cfe5d --- /dev/null +++ b/resources/themes/saasland/views/partials/header.blade.php @@ -0,0 +1,68 @@ + + +
+ @include('stylist-theme-saasland::partials.navigation-items', ['items' => $primaryNavigation]) + +
+ @auth + Dashboard + @if (Route::has('profile.edit')) + Profile + @endif +
+ @csrf + +
+ @else + @if (Route::has('login'))Log in@endif + @if (Route::has('register'))Get started@endif + @endauth +
+
diff --git a/resources/themes/saasland/views/partials/navigation-items.blade.php b/resources/themes/saasland/views/partials/navigation-items.blade.php new file mode 100644 index 0000000..c33491a --- /dev/null +++ b/resources/themes/saasland/views/partials/navigation-items.blade.php @@ -0,0 +1,20 @@ +@foreach ($items as $item) + @if (!empty($item['children'])) + + @else + + {{ $item['label'] }} + + @endif +@endforeach diff --git a/routes/web.php b/routes/web.php index e9437ee..7526173 100644 --- a/routes/web.php +++ b/routes/web.php @@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Route; use Modules\Administration\Http\Controllers\DashboardController; Route::get('/', function () { - return view('stylist-theme-welcome::welcome'); + return stylist_theme_view('saasland', 'home'); })->name('home'); Route::middleware(['auth', 'verified'])->group(function () {