# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## MANDATORY SESSION PROTOCOL (hard rules, never skip)

These rules override every other instruction in this file, in `rules.md`, and any urge to save tokens. They apply to every session and every task.

### 1. Before planning or changing anything: read the `.claude/` folder

Every file in `.claude/` has to be read before you plan, design or edit anything in a new session. The imports below load the current files automatically:

@.claude/context.md
@.claude/UI.md
@.claude/decisions.md

- If any file exists in `.claude/` that is not imported above, read it too and add an `@.claude/<file>` line to this list.
- If the imports did not load (the content is not in your context), read the files with the Read tool before doing anything else.
- Reading `.claude/*` is exempt from the "minimal token / inspect only related files" rule in `rules.md`. That rule still applies to everything outside `.claude/`.
- Treat the `.claude` files as a starting point: the code is the source of truth. If you find a mismatch, fix the file as part of the task.

### 2. After the work is done: update the appropriate `.claude` files

You decide which files a task touches. A task may touch several files or none. Do not write noise. Use this guide:

| File | Update it when… | How |
|---|---|---|
| `.claude/context.md` | Anything structural changes: table, column, index, FK, relationship, model, controller, service, repository, provider, middleware, route, view/layout structure, package or dependency, config/env, command, or a known quirk is found or fixed | Edit the affected section in place so the file always describes the **current** state (not a changelog) |
| `.claude/UI.md` | Anything visual or interactive changes: colour, font, token, component, page template, layout, spacing, animation, admin theme | Edit or append to the relevant section in place, adding new component recipes; keep exact values |
| `.claude/decisions.md` | A choice was made between alternatives: architecture, library, data model, design direction, process, or a reversal | **Append only**, using the template in that file (date, decision, context, options with pros/cons, why, consequences). Never rewrite old entries; a reversal is a new entry with `Supersedes:` |

- In the final message of every task, state which `.claude` files you updated (or that none needed changes).
- Keep the files compact (tables over prose, no duplication) so reading them every session stays cheap. The `.claude` folder replaces `structure.md`; do not maintain structure documentation anywhere else.
- If the user makes a decision during the task (even in passing), record it in `decisions.md` with the reasons they gave. If you are unsure of the reason, ask or write it as stated; never invent reasons.

## Project

Public website plus admin CMS for Srimanta Sankaradeva Kalakshetra (Guwahati). Laravel 12 / PHP 8.2, Blade views, Breeze auth, Bootstrap 5 on both the public site and admin. English/Assamese content. `README.md` is the stock Laravel README and says nothing about this project.

`rules.md` holds the project's working rules. Structure (routes, views, tables, joins, packages) lives in `.claude/context.md`, which replaced `structure.md`. `structure.md` is now only a pointer. Per `rules.md`, the code is the source of truth. The architecture notes below are a short summary; `.claude/context.md` has the full detail.

## Commands

```bash
composer dev                 # php artisan serve + queue:listen + pail + vite, via concurrently
npm run dev / npm run build  # Vite (entry points: resources/css/app.css, resources/js/app.js)
php artisan migrate          # never migrate:fresh, drop or truncate without approval (rules.md)
php artisan test             # Pest + PHPUnit
php artisan test --filter=test_pdf_picker_excludes_images_and_retains_old_input
vendor/bin/pest tests/Unit/AdminMediaPickerTest.php
vendor/bin/pint              # code style (no lint config beyond Pint defaults / .editorconfig)
```

- `.env.example` defaults to `DB_CONNECTION=sqlite`, `SESSION_DRIVER=database`, `QUEUE_CONNECTION=database` and `CACHE_STORE=database`. The `sessions`, `cache` and `jobs` tables must be migrated.
- In `phpunit.xml` the sqlite/`:memory:` overrides are commented out, and `tests/Pest.php` applies `RefreshDatabase` to `tests/Feature`. Feature tests therefore run against whatever database `.env` points to and wipe it. Set `DB_CONNECTION=sqlite` and `DB_DATABASE=:memory:` before running them, or point them at a throwaway DB.
- Test coverage is minimal: Breeze auth and profile tests, plus `tests/Unit/AdminMediaPickerTest.php`, which renders the `x-admin.media-picker` component.

## Architecture

### Two separate halves

1. **Admin CMS** (`/admin/*`, behind `auth`). It is database-backed and follows `Controller -> Service -> Repository -> Model`, as `rules.md` requires. There are 14 modules: categories, media-assets, books, artefacts, articles, news-events, events, newsletters, gallery-albums, gallery-images, featured-videos, documents, form-submissions and site-settings.
2. **Public site**. It is a mix of DB-backed lists/details, static Blade pages and static data arrays. The Service/Repository layering is **not** applied here.

### Admin module pattern

Each module has `Admin/{X}Controller`, `Services/{X}Service`, `Repositories/{X}Repository` and `Models/{X}`. Views are in `resources/views/admin/{kebab-plural}/` and extend `layouts.admin`. `routes/web.php` registers each one the same way: `Route::resource(...)->parameters(['x' => 'record'])`.

- The services and repositories are near-identical templates, not shared base classes. A service declares a `$fields` array of `name`/`type`/`required`. `validationRules()` and `prepareData()` are generated from it, along with the slug (`$slugSource`), `$uniqueColumns`, `$trackUsers` (sets `created_by`/`updated_by`) and `selectOptions()`. A repository declares `$relations` and `$searchColumns`. A change to a module usually means editing its `$fields` array and views, not writing new logic. Changing behaviour that applies to every module means editing each service.
- `Admin/Concerns/SupportsAdminFormPages` supplies `create`/`edit` and resolves the view folder from the controller name. Controllers validate with `$request->validate($this->service->validationRules($id))`.
- Records are passed to controllers by id (`int $record`), not by route-model binding.
- Media is stored once in `media_assets` and referenced by `*_image_id` or `*_file_id` columns. Forms use the `x-admin.media-picker` component, and `MediaAssetController` has the `search-images` endpoint.
- Content tables are bilingual (`*_en` / `*_as` columns), soft-deleted, and use a `status` of `draft`, `published` or `archived`. Public queries filter on `status = 'published'`.
- `rules.md` requires meaningful backend logging, with module log files declared in `config/logs.php`. In practice only `MediaAssetController` calls `Log::`, so new backend work should add it.
- Spatie permission tables exist, but the package is not installed and `User` does not use roles. `ActivityLog` and `VisitorStat` models exist but nothing writes to them yet.

### Public site: legacy and v3 coexist

Two generations of public templates are live:

- **Current ("v3")**: `resources/views/frontend-v3/` (`landing/` for head, header, footer and scripts; `pages/layout.blade.php` for the page shell; `about/`; `pages/{museum,auditoriums,events,...}`). It is served from `/` (the homepage; legacy `landing` is on `/home-v3`), `FrontendV3PageController` and many `Route::view()` entries. Pages `@extends('frontend-v3.pages.layout')` and fill `@section('page')`. Most museum and auditorium pages are standalone Blade files with inline data (event pages are DB-backed: `events` table, `/events/{slug}`) and scoped `styles`/`scripts` partials, routed by hand in `routes/web.php`, each with a `title` and a `frontend-v3.*` route name.
- **Legacy**: `layouts/frontend.blade.php`, `resources/views/frontend/`, `frontend-v2/`, `pages/`, and the `landing` / `landing-v2` views. These run through `FrontendStaticPageController` and the static arrays in `resources/data/frontend-static.php`. The old books, artefacts, gallery, booking, contact and tenders pages now live under `/legacy-*` and render the static demo arrays in that controller. The live `/books`, `/artefacts`, `/gallery` and similar URLs belong to v3.

Things to know before touching routes or views:

- **Route order matters.** Specific URLs such as `/museums/purbajyoti-sangrahalaya`, `/halls-auditoriums/council-chamber` and `/gallery/portrait-gallery` are registered before the generic `/{slug}` routes they would otherwise hit. Redirects keep old slugs working. Register new specific pages above the wildcard.
- `FrontendV3PageController` queries Eloquent models directly. Its helpers `assetUrl()` and `localized()` resolve `MediaAsset` paths (`storage/`, `images/` and full-URL prefixes, then the `public` disk), and `submitForm` stores contact and booking posts in `form_submissions`. Static presentation data lives in `resources/data/frontend-v3-*.php` and is loaded with `require resource_path(...)`.
- **Language handling.** The `/language/{locale}` route stores the locale in the session, and the controllers call `App::setLocale(session('locale', ...))`. The dictionary switcher (`frontend/partials/language-script.blade.php`, with `lang/en/landing.php` and `lang/as/landing.php`) is loaded only by the legacy layout and the `landing` / `landing-v2` views. The v3 layout does not load it. Per `rules.md`, new public text goes into those existing dictionaries rather than a second translation system.
- Images used by the static pages live in `public/images/` (large per-section folders). Uploaded media goes through the `public` disk, so it needs `php artisan storage:link`.

### Conventions from `rules.md`

- New pages reuse the homepage design (header, footer, colours, typography, spacing, breakpoints) and do not redesign global UI.
- Do not modify unrelated code. Report other issues you find instead of fixing them.
- Stop and ask before critical changes: dropping or renaming columns, changing foreign keys, authentication changes, removing functionality, breaking route changes, large refactors, and mass delete or update operations.
- Never invent columns or relationships. Check them against the models and migrations. Avoid N+1 queries by eager loading and paginating.
- Outside `.claude/`, inspect only the files for the module you are changing, not the whole repo.

### Local-only files

`.vscode/` (including an `sftp.json` with deploy credentials) is gitignored. Do not copy its contents into code, docs or commits.
