# Digital PR Project Architecture Notes This project is built as a CMS-driven marketing, portfolio, and blog website. It uses Laravel 12 with Inertia + React to render the public site, while the admin area manages content records, media, SEO metadata, and form submissions. ## Stack - Backend: Laravel 12, PHP 8.2+, Eloquent ORM. - Frontend: React 19, TypeScript, Inertia.js 2, Vite 7. - Styling/animation: Tailwind CSS 4, GSAP, Lenis, Swiper, assorted React UI helpers. - Auth/CMS: Laravel Breeze-style auth routes under `/admin`. - Caching: `spatie/laravel-responsecache` for cacheable public pages. - Tests: Pest via `php artisan test`. - Build tooling: Composer for PHP, npm/Vite for frontend assets. ## Top-Level Structure - `app/Http/Controllers`: public page controllers, admin CRUD controllers, media, sitemap, and form submission handlers. - `app/Models`: Eloquent models for CMS entities such as `Blog`, `Portfolio`, `Media`, `Service`, `Fact`, `Partner`, `Testimonial`, settings models, and submissions. - `app/Providers/AppServiceProvider.php`: registers content cache observers and globally shares CMS content with all Inertia pages. - `app/Observers/ContentCacheObserver.php`: clears the response cache when public-facing content models are saved/deleted/restored. - `routes/web.php`: public routes, form route, sitemap route, admin route include, and catch-all blog post route. - `routes/admin.php`: `/admin` auth and CMS CRUD routes. - `resources/js`: React/Inertia application, pages, components, data fallbacks, types, hooks, and browser bootstrap. - `resources/views/app.blade.php`: Inertia root Blade template. - `database/migrations`: tables for users, media, portfolios, services, blog sections, page settings, meta tags, FAQs, and submissions. - `database/seeders`: initial CMS content and admin user seeders. - `public`: static assets, uploaded-public references, robots, theme CSS, fonts, images. ## Request Flow 1. A web request enters through `routes/web.php`. 2. Laravel routes public pages to controllers such as `HomeController`, `ServicePageController`, `BlogController`, and `SitemapController`. 3. Controllers query Eloquent models and return `Inertia::render(...)` responses. 4. `HandleInertiaRequests` shares auth, flash messages, and SEO metadata with all Inertia pages. 5. `AppServiceProvider` additionally shares global CMS data such as site settings, footer, CTA, home settings, facts, services, approaches, marquee items, awards, testimonials, partners, featured portfolios, and featured blogs. 6. React resolves page components from `resources/js/Pages/**/*.tsx` in `resources/js/app.tsx`. 7. Vite compiles `resources/js/app.tsx`; SSR entry exists at `resources/js/ssr.tsx`. ## Public Routes Cacheable public routes are wrapped in `Spatie\ResponseCache\Middlewares\CacheResponse`: - `/` -> `HomeController@index` -> `Home` - `/about` -> `HomeController@about` -> `About` - `/services` -> `ServicePageController@index` -> `Services/Index` - `/portfolio` -> `HomeController@portfolio` -> `Portfolio/Index` - `/portfolio/{slug}` -> `HomeController@projectDetails` -> `Portfolio/Show` - `/blog` -> `HomeController@blog` -> `Blog/Index` - `/{slug}` -> `BlogController@show` -> `Blog/Show` Non-cached public/dynamic routes: - `/sitemap.xml` -> `SitemapController@index` - `/api/blog-content/{slug}` -> `BlogContentController@fetchContent` - `/contact` -> `HomeController@contact` - `POST /form-submissions` -> `FormSubmissionController@store` Important routing detail: blog posts are served by the final catch-all `/{slug}` route, so it must stay after specific public and admin routes. ## Admin CMS All admin routes live in `routes/admin.php` and use the `/admin` prefix. - Guest admin auth routes: login, forgot password, reset password. - Authenticated routes: email verification, password confirmation/update, logout. - Authenticated + verified CMS routes: dashboard, profile, media, portfolios, facts, services, approaches, marquee items, awards, testimonials, partners, blogs, home settings, studio page settings, team members, footer settings, CTA settings, site settings, services page settings, contact page settings, meta tags, FAQs, and form submissions. Admin pages are React/Inertia pages under `resources/js/Pages/**/Admin` and `resources/js/Pages/Admin/**`. ## Core Data Model - `Media`: central asset table. Uploads go to the public disk under `media/{page}/{section}`. The computed `url` accessor serves `img/...` paths from `public` and all other paths from `storage/...`. - `Portfolio`: case studies/projects with thumbnail, hero image, solution image, gallery IDs, tags, publication fields, and generated slug fallback. - `Blog`: posts with structured title parts, tags, thumbnail/featured images, publication flags, featured/sub-featured flags, and ordered `BlogSection` children. - `BlogSection`: flexible content sections for blog posts; expected types include `content`, `image`, `gallery`, and `slider`. - `Service`, `Approach`, `Fact`, `Award`, `Partner`, `Testimonial`, `MarqueeItem`: repeatable CMS blocks shown across public pages. - Settings models: `SiteSetting`, `HomeSetting`, `StudioPageSetting`, `ServicesPageSetting`, `ContactPageSetting`, `FooterSetting`, `CtaSetting`. - `MetaTag`: per-URL SEO metadata loaded in `HandleInertiaRequests` for non-admin pages. - `FormSubmission`: stores contact/newsletter submissions with IP, user agent, and status. Most CMS models expose local scopes such as `active()`, `ordered()`, `published()`, or line/status filters. Prefer these existing scopes when querying content. ## Frontend Organization - Entry point: `resources/js/app.tsx`. - Page resolution: Inertia maps names like `Home`, `Blog/Show`, or `Portfolio/Index` to `resources/js/Pages/{name}.tsx`. - Shared layouts: `resources/js/Layouts`. - Shared UI/content components: `resources/js/Components`. - Static fallback/reference data: `resources/js/data`. - Type definitions: `resources/js/types`. - Routing helper: `resources/js/lib/route.ts` and generated Ziggy config at `resources/js/ziggy.js`. The UI uses many CMS-provided props. When adding or changing content shown on many pages, check both: - Controller-specific props in `app/Http/Controllers`. - Global Inertia props in `AppServiceProvider`. ## SEO `HandleInertiaRequests` resolves `seoMeta` from `MetaTag::getByUrl($url)` for non-admin routes. Some controllers pass page-specific `seoMeta` directly, especially blog and portfolio detail pages. Controller-level props are intended to override shared metadata. ## Cache Behavior Public cacheable pages use Spatie response cache. `ContentCacheObserver` clears the full-page cache when observed content models change. `AppServiceProvider` attaches this observer to public-facing models. If adding a new public-facing content model that affects cached pages, register it in the `$contentModels` array in `AppServiceProvider`. ## Static Snapshot Command `app/Console/Commands/GenerateStaticPages.php` defines: ```bash php artisan pages:generate-static --force --url=https://example.com ``` It fetches public page HTML and writes snapshots to `public/static`. Review this command carefully before relying on it for blog snapshots, because public blog posts are routed as `/{slug}` in `routes/web.php`. ## Common Commands ```bash composer install npm install php artisan migrate php artisan db:seed composer run dev npm run dev npm run build npm run typecheck php artisan test vendor/bin/pint ``` `composer run dev` starts Laravel, queue listener, logs, and Vite concurrently. ## Development Notes For Future Agents - Prefer Eloquent relationships and existing model scopes over ad hoc queries. - Public page changes often require checking both backend props and React page/component prop expectations. - Keep the `/{slug}` catch-all route last. - Do not cache contact/forms/API routes unless their dynamic behavior is intentionally changed. - Media references usually point to `Media` records and use computed `url` accessors. - When a CMS edit changes public content, response cache should be cleared automatically only for models registered in `AppServiceProvider`. - Root README is still Laravel boilerplate; use this file and the codebase as the project-specific guide.