feat: initial core/php-client — SaaS customer dashboard
Extracts Core\Front\Client (authenticated dashboard, client routes, Livewire components) from core/php. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
97909bec75
8 changed files with 335 additions and 0 deletions
22
LICENSE
Normal file
22
LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
European Union Public Licence
|
||||
Version 1.2
|
||||
|
||||
EUPL (c) the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the "EUPL") applies to the Work (as
|
||||
defined below) which is provided under the terms of this Licence. Any use
|
||||
of the Work, other than as authorised under this Licence is prohibited (to
|
||||
the extent such use is covered by a right of the copyright holder of the
|
||||
Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the
|
||||
EUPL.
|
||||
|
||||
For the full licence text, see:
|
||||
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
|
||||
24
composer.json
Normal file
24
composer.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "lthn/client",
|
||||
"description": "SaaS customer dashboard — authenticated client frontend",
|
||||
"license": "EUPL-1.2",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"lthn/php": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Core\\Front\\Client\\": "src/Core/Front/Client/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"core/php-client": "self.version"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
24
src/Core/Front/Client/Blade/dashboard.blade.php
Normal file
24
src/Core/Front/Client/Blade/dashboard.blade.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<div class="max-w-4xl mx-auto px-6 py-12">
|
||||
<h1 class="text-3xl font-bold text-white mb-4">Your Namespace</h1>
|
||||
<p class="text-zinc-400 mb-8">Manage your space on the internet.</p>
|
||||
|
||||
<div class="grid gap-6">
|
||||
{{-- Namespace Overview --}}
|
||||
<div class="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
|
||||
<h2 class="text-lg font-semibold text-white mb-2">Overview</h2>
|
||||
<p class="text-zinc-500 text-sm">Your namespace details will appear here.</p>
|
||||
</div>
|
||||
|
||||
{{-- Quick Actions --}}
|
||||
<div class="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
|
||||
<h2 class="text-lg font-semibold text-white mb-2">Quick Actions</h2>
|
||||
<p class="text-zinc-500 text-sm">Edit your bio, view analytics, and more.</p>
|
||||
</div>
|
||||
|
||||
{{-- Recent Activity --}}
|
||||
<div class="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
|
||||
<h2 class="text-lg font-semibold text-white mb-2">Recent Activity</h2>
|
||||
<p class="text-zinc-500 text-sm">Your recent activity will appear here.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
85
src/Core/Front/Client/Blade/layouts/app.blade.php
Normal file
85
src/Core/Front/Client/Blade/layouts/app.blade.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ $title ?? 'Dashboard' }} - lt.hn</title>
|
||||
|
||||
{{-- Fonts --}}
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700&display=swap" rel="stylesheet">
|
||||
|
||||
{{-- Font Awesome --}}
|
||||
<link rel="stylesheet" href="/vendor/fontawesome/css/all.min.css">
|
||||
|
||||
@vite(['resources/css/admin.css', 'resources/js/app.js'])
|
||||
@fluxAppearance
|
||||
</head>
|
||||
<body class="font-inter antialiased bg-[#070b0b] text-[#cccccb] min-h-screen">
|
||||
{{-- Header --}}
|
||||
<header class="sticky top-0 z-50 border-b border-[#40c1c5]/10 bg-[#070b0b]/95 backdrop-blur-sm">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-14">
|
||||
{{-- Logo + Bio link --}}
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="{{ url('/') }}" class="text-xl font-bold text-white">lt.hn</a>
|
||||
@if(isset($bioUrl))
|
||||
<span class="text-[#cccccb]/40">/</span>
|
||||
<a href="{{ url('/' . $bioUrl) }}" class="text-sm text-[#40c1c5] hover:text-[#5dd1d5] transition">
|
||||
{{ $bioUrl }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Nav + User menu --}}
|
||||
<div class="flex items-center gap-6">
|
||||
@if(isset($bioUrl))
|
||||
<nav class="flex items-center gap-1">
|
||||
@php
|
||||
$currentPath = request()->path();
|
||||
$navItems = [
|
||||
['url' => "/{$bioUrl}/settings", 'label' => 'Editor', 'icon' => 'fa-pen-to-square'],
|
||||
['url' => "/{$bioUrl}/analytics", 'label' => 'Analytics', 'icon' => 'fa-chart-line'],
|
||||
['url' => "/{$bioUrl}/submissions", 'label' => 'Submissions', 'icon' => 'fa-inbox'],
|
||||
['url' => "/{$bioUrl}/qr", 'label' => 'QR Code', 'icon' => 'fa-qrcode'],
|
||||
];
|
||||
@endphp
|
||||
@foreach($navItems as $item)
|
||||
<a href="{{ url($item['url']) }}"
|
||||
class="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm transition
|
||||
{{ $currentPath === ltrim($item['url'], '/')
|
||||
? 'bg-[#40c1c5]/10 text-[#40c1c5]'
|
||||
: 'text-[#cccccb]/60 hover:text-white hover:bg-white/5' }}">
|
||||
<i class="fa-solid {{ $item['icon'] }} text-xs"></i>
|
||||
<span class="hidden sm:inline">{{ $item['label'] }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
@endif
|
||||
|
||||
@auth
|
||||
<div class="flex items-center gap-4 pl-4 border-l border-[#40c1c5]/10">
|
||||
<a href="{{ url('/dashboard') }}" class="text-sm text-[#cccccb]/60 hover:text-[#40c1c5] transition hidden md:inline">
|
||||
Dashboard
|
||||
</a>
|
||||
<a href="{{ url('/logout') }}" class="text-sm text-[#cccccb]/60 hover:text-white transition">
|
||||
Sign out
|
||||
</a>
|
||||
</div>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{-- Main content --}}
|
||||
<main class="min-h-[calc(100vh-3.5rem)]">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@fluxScripts
|
||||
</body>
|
||||
</html>
|
||||
69
src/Core/Front/Client/Boot.php
Normal file
69
src/Core/Front/Client/Boot.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?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\Front\Client;
|
||||
|
||||
use Core\Headers\SecurityHeaders;
|
||||
use Core\LifecycleEventProvider;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* Client frontage - namespace owner dashboard.
|
||||
*
|
||||
* For SaaS customers managing their namespace (personal workspace).
|
||||
* Not the full Hub/Admin - just YOUR space on the internet.
|
||||
*
|
||||
* Hierarchy:
|
||||
* - Core/Front/Web = Public (anonymous, read-only)
|
||||
* - Core/Front/Client = SaaS customer (authenticated, namespace owner)
|
||||
* - Core/Front/Admin = Backend admin (privileged)
|
||||
* - Core/Hub = SaaS operator (Host.uk.com control plane)
|
||||
*
|
||||
* A namespace is tied to a URI/handle (lt.hn/you, you.lthn).
|
||||
* A workspace (org) can manage multiple namespaces.
|
||||
* A personal workspace IS your namespace.
|
||||
*/
|
||||
class Boot extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Configure client middleware group.
|
||||
*/
|
||||
public static function middleware(Middleware $middleware): void
|
||||
{
|
||||
$middleware->group('client', [
|
||||
\Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
SecurityHeaders::class,
|
||||
'auth',
|
||||
]);
|
||||
}
|
||||
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
// Register client:: namespace for client dashboard components
|
||||
$this->loadViewsFrom(__DIR__.'/Blade', 'client');
|
||||
Blade::anonymousComponentPath(__DIR__.'/Blade', 'client');
|
||||
|
||||
// Fire ClientRoutesRegistering event for lazy-loaded modules
|
||||
LifecycleEventProvider::fireClientRoutes();
|
||||
}
|
||||
}
|
||||
52
src/Core/Front/Client/README.md
Normal file
52
src/Core/Front/Client/README.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Core/Front/Client
|
||||
|
||||
SaaS customer dashboard for namespace owners.
|
||||
|
||||
## Concept
|
||||
|
||||
```
|
||||
Core/Front/Web → Public (anonymous, read-only)
|
||||
Core/Front/Client → SaaS customer (authenticated, namespace owner) ← THIS
|
||||
Core/Front/Admin → Backend admin (privileged)
|
||||
Core/Hub → SaaS operator (Host.uk.com control plane)
|
||||
```
|
||||
|
||||
## Namespace vs Workspace
|
||||
|
||||
- **Namespace** = your identity, tied to a URI/handle (lt.hn/you, you.lthn)
|
||||
- **Workspace** = management container (org/agency that can own multiple namespaces)
|
||||
- **Personal workspace** = IS your namespace (1:1 for solo users)
|
||||
|
||||
A user with just a personal workspace uses **Client** to manage their namespace.
|
||||
An org workspace with multiple namespaces uses **Hub** for team management.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Bio page editor (lt.hn/you)
|
||||
- Analytics dashboard (your stats)
|
||||
- Domain management (custom domains, web3)
|
||||
- Settings (profile, notifications)
|
||||
- Boost purchases (expand namespace entitlements)
|
||||
|
||||
## Not For
|
||||
|
||||
- Team/org management (use Hub)
|
||||
- Multi-namespace management (use Hub)
|
||||
- Backend admin tasks (use Admin)
|
||||
- Public viewing (use Web)
|
||||
|
||||
## Middleware
|
||||
|
||||
```php
|
||||
Route::middleware('client')->group(function () {
|
||||
// Namespace owner routes
|
||||
});
|
||||
```
|
||||
|
||||
## Views
|
||||
|
||||
```blade
|
||||
@extends('client::layouts.app')
|
||||
|
||||
<x-client::component />
|
||||
```
|
||||
30
src/Core/Front/Client/Routes/client.php
Normal file
30
src/Core/Front/Client/Routes/client.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Core PHP Framework
|
||||
*
|
||||
* Licensed under the European Union Public Licence (EUPL) v1.2.
|
||||
* See LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Front\Client\View\Dashboard;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Client Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Routes for namespace owners managing their personal workspace.
|
||||
| Uses 'client' middleware group (authenticated, namespace owner).
|
||||
|
|
||||
*/
|
||||
|
||||
Route::middleware('client')->group(function () {
|
||||
// Dashboard
|
||||
Route::get('/dashboard', Dashboard::class)->name('client.dashboard');
|
||||
|
||||
// Additional routes can be registered via ClientRoutesRegistering event
|
||||
});
|
||||
29
src/Core/Front/Client/View/Dashboard.php
Normal file
29
src/Core/Front/Client/View/Dashboard.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?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\Front\Client\View;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* Client dashboard - namespace owner home.
|
||||
*/
|
||||
#[Title('Dashboard')]
|
||||
class Dashboard extends Component
|
||||
{
|
||||
public function render(): View
|
||||
{
|
||||
return view('client::dashboard')
|
||||
->layout('client::layouts.app');
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue