This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Laravel 13 + PHP 8.3 backend with React 19 + TypeScript frontend connected via Inertia.js. Default database is MariaDB (DDEV); SQLite also supported via .env. No Filament — all UI is custom React.
# Full project setup (install deps, .env, key, migrate, build)
composer run setup
# Development (Laravel server + queue + logs + Vite, all concurrent)
composer run dev
# Tests
composer run test
# Frontend only
npm run dev # Vite dev server
npm run build # TypeScript check + Vite production build
npm run lint # ESLint with auto-fixUsers belong to Projects (many-to-many via project_user pivot with JSON permissions and active flag). Each Project has Domains (custom domains with redirect rules) and URLs (short links with slug, target, type, password, expiry). Statistics track clicks per URL (IP, geo, device, referrer).
- Register → user logged in → redirected to
/, which forwards to their first project's dashboard - Login → redirected to
/on success (no email verification) - Root
/→ authenticated users go to their first project's dashboard, orabort(403)if they belong to no project; guests go to login - Password reset uses Laravel's built-in
Password::sendResetLink()/Password::reset() - All project routes protected by
authandProjectBindingMiddleware
Controllers: AuthController, PasswordResetController, DashboardController
ProjectBindingMiddleware resolves the {project} route parameter, checks User::canAccessProject(), then:
- Makes the
Projectmodel available via$request->get('project') - Shares it to Inertia via
HandleInertiaRequests::share()ascurrentProject - Also shares
projects(all user projects) for the tenant switcher
Route structure:
/auth/*— guest-only auth pages/project/{project}/*— tenant-scoped app pages (auth + ProjectBindingMiddleware)
GuestLayout(resources/js/Layouts/GuestLayout.tsx) — two-column layout for auth pages; left panel is branding, right panel is the form cardAppLayout(resources/js/Layouts/AppLayout.tsx) — full-height flex withSidebar+ scrollable<main>; readscurrentProjectfrom shared Inertia props
Sidebar → navigation items + ProjectSwitcher + UserMenu (bottom). No top navigation bar.
ProjectSwitcher renders a dropdown from the shared projects prop and navigates to /project/{id}/dashboard on switch. UserMenu shows user initials avatar + logout.
Every page receives:
auth.user— authenticated user (or null)projects— all projects the user belongs to (for switcher)currentProject— the resolved project for the current route (null on auth pages)
resources/js/types/index.d.ts exports User, Project, and PageProps<T>.
UrlStatus and RedirectType use a plain label(): string method — not the old Filament HasLabel interface.
UrlObserver auto-sets user_id on URL creation. Register new observers in AppServiceProvider via #[ObservedBy] attribute on the model.
Always write scripts/files/docs in English