diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..7aeba0b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,156 @@ +# AGENTS.md + +Agent guidance for working in this repository (`mainty`). + +## Project Snapshot + +- Stack: PHP 8+, SQLite, Apache, Tailwind via CDN, Bootstrap Icons via CDN. +- Architecture: lightweight MVC-style structure (no Composer, no framework). +- Entry point: `index.php`. +- Routing: `core/Router.php` maps routes to `Controller@method`. +- Data layer: PDO SQLite through singleton in `core/Database.php`. +- Runtime config: constants and debug flags in `config.php`. +- Deployment option: Docker (`Dockerfile`, `docker-compose.yml`) or traditional Apache/PHP host. + +## Repository Layout + +- `index.php`: bootstrap, environment checks, route registration, request dispatch. +- `core/`: base infrastructure (`Controller`, `Router`, `Database`). +- `controllers/`: request handlers, auth/setup guards, redirects. +- `models/`: database queries and persistence logic. +- `views/`: server-rendered PHP templates and inline JS. +- `data/`: SQLite DB location (`data/mainty.db`), ignored by Git. +- `README.md`: user-facing setup notes. +- `DOCKER.md`: Docker workflow and troubleshooting. + +## Build, Lint, and Test Commands + +This repo currently has no formal package manager scripts (`composer.json`, `package.json`) and no configured PHPUnit/Pest suite. + +### Local runtime checks + +- Start app with Docker: + - `docker-compose up -d` +- Rebuild and start: + - `docker-compose up -d --build` +- Stop containers: + - `docker-compose down` +- Follow logs: + - `docker-compose logs -f` +- Open app: + - `http://localhost:8080` + +### Linting (available today) + +- Lint a single PHP file: + - `php -l path/to/file.php` +- Lint all PHP files from repo root: + - `find . -name "*.php" -print0 | xargs -0 -n1 php -l` + +### Tests (current state) + +- There is no automated test suite committed in this repository at this time. +- Use targeted syntax checks plus manual verification in browser for affected flows. + +### Single-test guidance + +- Since no test framework is configured, there is no native "run one unit test" command yet. +- Closest equivalent today: + - Run syntax check on changed file: `php -l path/to/changed.php` + - Validate one user flow manually (for example setup/login/add vehicle). +- If PHPUnit is introduced later, single-test pattern should be: + - `vendor/bin/phpunit tests/Path/To/TestFile.php` + - or `vendor/bin/phpunit --filter testMethodName` + +## Development Workflow for Agents + +- Prefer small, focused edits in the relevant layer (controller/model/view). +- Preserve existing architecture; avoid introducing frameworks or heavy abstractions. +- Keep route definitions centralized in `index.php` unless project conventions change. +- For DB changes, update `Database::initialize()` schema and ensure migration path for existing DBs. +- Validate impact on setup/auth guards (`requireSetup()`, `requireAuth()`). +- When touching rendered output, maintain existing Tailwind + Bootstrap Icons approach. + +## Code Style and Conventions + +The project does not include an enforced formatter. Follow established in-repo patterns. + +### PHP formatting + +- Use 4-space indentation; no tabs. +- Opening braces are on the same line for classes, methods, and control structures. +- Keep method visibility explicit (`public`, `private`, `protected`). +- Prefer strict return types and parameter types where already used. +- Add blank lines between logical blocks to keep controllers readable. + +### Imports and file organization + +- There are no namespaces and no `use` imports currently. +- Autoloading is manual via `spl_autoload_register` in `index.php`. +- New class files should be placed in one of: + - `core/` + - `controllers/` + - `models/` +- Class name must match filename (e.g., `VehicleController` in `controllers/VehicleController.php`). + +### Types and data handling + +- Follow existing scalar type hints (`string`, `int`, `array`, `bool`, `void`). +- For IDs from route params, cast to int in controllers before model calls. +- Treat user input as untrusted; trim and validate before persistence. +- Use nullable values consistently when optional fields are absent. + +### Naming conventions + +- Classes: PascalCase (`MaintenanceController`, `QuickTask`). +- Methods/functions: camelCase (`changePassword`, `getByVehicleId`). +- Variables/properties: camelCase (`$maintenanceItems`, `$quickTaskModel`). +- DB columns: snake_case (`license_plate`, `updated_at`). +- Route paths: kebab/snake mix already exists; follow existing route patterns. + +### Database and SQL conventions + +- Use prepared statements for variable-bound SQL (existing standard). +- Keep SQL readable with multiline strings where complex. +- Return arrays from model queries (`fetchAll()`/`fetch()`) per current style. +- Keep persistence logic inside models; avoid SQL in controllers/views. + +### Error handling and user feedback + +- Controllers use session flash messages (`$_SESSION['error']`, `$_SESSION['success']`). +- Redirect after mutations instead of rendering directly. +- For JSON endpoints, return structured JSON via `Controller::json()`. +- Internal failures are currently handled with basic `die()` or fallback messages. +- Do not leak sensitive data in production; respect `DEBUG` flag in `config.php`. + +### Security and output escaping + +- Escape user-facing output in views with `htmlspecialchars()`. +- Keep password handling through `password_hash()` and `password_verify()`. +- Maintain auth checks on protected routes. +- Be cautious with direct echo of exception messages. + +### Frontend conventions in views + +- Server-rendered PHP templates with inline Tailwind classes. +- Keep interactions lightweight with inline ` +``` + +Expected output: +- `` has `dark` class before body render when resolved theme is dark. +- `data-theme` is set to `dark` or `light`. + +- [ ] **Step 3: Implement `head` shared CSS tokens and semantic utility classes** + +```php + +``` + +Expected output: +- Shared classes exist for page/surface/text/border/input mappings. +- Toggle has fixed top-right placement and z-index 50. + +- [ ] **Step 4: Implement `body` output with toggle markup and no-JS fallback** + +```php + + +``` + +- [ ] **Step 5: Implement `body` behavior script with cookie helpers and state sync** + +```php + +``` + +Expected output: +- Click toggles theme immediately, updates icon, `aria-label`, and `title`. +- Cookie write attempt does not block visual switch if persistence is restricted. + +- [ ] **Step 6: Run syntax check for new partial** + +Run: `php -l views/partials/theme.php` + +Expected: `No syntax errors detected in views/partials/theme.php`. + +- [ ] **Step 7: Commit Task 1** + +```bash +git add views/partials/theme.php +git commit -m "feat: add shared theme partial with cookie-based dark mode" +``` + +### Task 2: Integrate theme partial into login and setup views + +**Files:** +- Modify: `views/login.php` +- Modify: `views/setup.php` +- Test: `/login`, `/setup` + +- [ ] **Step 1: Insert head include in `views/login.php`** + +```php + +``` + +Placement: in `
`, after Tailwind/bootstrap includes, before ``. + +- [ ] **Step 2: Insert body include in `views/login.php`** + +```php + +``` + +Placement: before ``. + +- [ ] **Step 3: Apply color-only updates in `views/login.php`** + +Apply concrete replacements: +- `` -> `` +- login card `bg-white` -> `theme-surface` +- heading `text-gray-800` -> `theme-text` +- label text `text-gray-700` -> `theme-text` +- input `border-gray-300` -> add `theme-input theme-border` + +Expected outcome: login card, labels, and input fields are readable in both themes without layout shift. + +- [ ] **Step 4: Insert head include in `views/setup.php`** + +```php + +``` + +Placement: in ``, after Tailwind/bootstrap includes, before ``. + +- [ ] **Step 5: Insert body include in `views/setup.php`** + +```php + +``` + +Placement: before ``. + +- [ ] **Step 6: Apply color-only updates in `views/setup.php`** + +Apply concrete replacements: +- `` -> `` +- setup card `bg-white` -> `theme-surface` +- title and labels `text-gray-*` -> `theme-text` / `theme-text-muted` +- password inputs `border-gray-300` -> add `theme-input theme-border` + +Expected outcome: setup page remains visually consistent, with readable text/inputs in both themes. + +- [ ] **Step 7: Run syntax checks for auth/setup views** + +Run: `php -l views/login.php && php -l views/setup.php` + +Expected: both report `No syntax errors detected`. + +- [ ] **Step 8: Manual verification on `/login` and `/setup`** + +Expected: +- toggle appears fixed top-right +- keyboard Tab focuses toggle with visible focus style +- Enter/Space toggles theme +- icon and `aria-label`/`title` change each toggle +- reload preserves preference + +- [ ] **Step 9: Commit Task 2** + +```bash +git add views/login.php views/setup.php +git commit -m "feat: integrate dark mode toggle on auth and setup views" +``` + +## Chunk 2: Main Views, Export Exception, and Final Verification + +### Task 3: Integrate theme partial into home view + +**Files:** +- Modify: `views/index.php` +- Test: `/home` + +- [ ] **Step 1: Add head include in `views/index.php`** + +```php + +``` + +Placement requirement: after Tailwind/bootstrap includes and before ``. + +Expected outcome: exactly one head include in required position. + +- [ ] **Step 2: Add body include in `views/index.php`** + +```php + +``` + +Placement requirement: before local page scripts and before ``. + +Expected outcome: exactly one body include in required position. + +- [ ] **Step 3: Update page and header surfaces in `views/index.php`** + +Concrete updates: +- body wrapper `bg-gray-100` -> `theme-page` +- header/card wrappers `bg-white` -> `theme-surface` +- heading/body text `text-gray-*` -> `theme-text` / `theme-text-muted` + +Expected outcome: page shell and header are readable in both themes. + +- [ ] **Step 4: Update table surfaces in `views/index.php`** + +Concrete updates: +- table header `` -> use `theme-surface-alt theme-border` +- table body divider `divide-gray-200` -> `theme-border` +- table cell text classes `text-gray-500`, `text-gray-600`, `text-gray-900` -> `theme-text`/`theme-text-muted` + +Expected outcome: table head/body text and borders remain readable in both themes. + +- [ ] **Step 5: Update vehicle cards/list rows in `views/index.php`** + +Concrete updates: +- grid cards `bg-white` -> `theme-surface` +- list container `bg-white` -> `theme-surface` +- row hover `hover:bg-gray-50` -> `hover:opacity-95` and apply `theme-surface-alt` on row container where needed +- small metadata text `text-gray-*` -> `theme-text-muted` + +Expected outcome: vehicle cards/list rows remain readable in both themes. + +- [ ] **Step 6: Update modal container and input surfaces in `views/index.php`** + +Concrete updates: +- modal panel `bg-white` -> `theme-surface` +- modal close/cancel gray text -> `theme-text-muted` +- inputs with `border-gray-300` -> add `theme-input theme-border` +- optional helper text gray shades -> `theme-text-muted` + +Expected outcome: cards, table, and modal inputs remain readable/usable in both themes. + +- [ ] **Step 7: Verify and adjust flash message contrast in `views/index.php`** + +Concrete updates: +- keep existing light-mode classes for base state (`bg-red-100 border-red-400 text-red-700`, `bg-green-100 border-green-400 text-green-700`) +- add dark-only variants (using `dark:` classes or equivalent conditional class strategy) for error/success contrast in dark mode +- keep existing rounded/padding/layout classes unchanged + +Expected outcome: error and success flash messages are legible and visually distinct in both light and dark modes. + +- [ ] **Step 8: Run syntax check for `views/index.php`** + +Run: `php -l views/index.php` + +Expected: `No syntax errors detected in views/index.php`. + +- [ ] **Step 9: Manual verification for `/home`** + +Expected: +- theme toggles correctly +- theme persists after reload +- cards/tables/modals remain readable in both themes + +- [ ] **Step 10: Commit Task 3** + +```bash +git add views/index.php +git commit -m "feat: integrate dark mode support in home view" +``` + +### Task 4: Integrate theme partial into vehicle detail view + +**Files:** +- Modify: `views/vehicle.php` +- Test: `/vehicles/{id}` + +- [ ] **Step 1: Add head include in `views/vehicle.php`** + +```php + +``` + +Placement requirement: after Tailwind/bootstrap includes and before ``. + +Expected outcome: exactly one head include in required position. + +- [ ] **Step 2: Add body include in `views/vehicle.php`** + +```php + +``` + +Placement requirement: before page-local scripts and before ``. +- Inject shared theme CSS variables and a concise set of utility mappings for common surfaces and text colors. +- Expose small JS helpers for: + - reading cookie, + - writing cookie, + - resolving effective theme, + - updating UI state/icon/labels, + - toggling theme on click. + +Why: + +- Keeps behavior consistent across all views. +- Avoids duplicating script/CSS/button logic in each template. +- Reduces maintenance cost for future theme adjustments. + +Interface contract: + +- The partial is included with an explicit section selector: + - `$themeSection = 'head'; include __DIR__ . '/partials/theme.php';` + - `$themeSection = 'body'; include __DIR__ . '/partials/theme.php';` +- `'head'` section outputs only bootstrap script + shared theme styles. +- `'body'` section outputs only floating toggle markup + behavior wiring script. +- Any other/missing section outputs nothing (safe no-op). + +## 2) View Integration Points + +Integrate the partial into each in-scope view. + +- Include contract per themed view: + - In `` (after Tailwind CDN script/link and before ``): set `$themeSection = 'head'` and include `views/partials/theme.php`. + - In `` (before page-local scripts and before ``): set `$themeSection = 'body'` and include `views/partials/theme.php`. + - Partial integration must not require any page-specific IDs. + - Partial must be self-contained and safe to include on all in-scope views without per-view conditionals. + +Each view keeps existing layout and structure; only color semantics are adjusted where required for dark compatibility. + +## 3) Export Exception + +`views/export.php` remains standalone light styling: + +- No theme partial include. +- No floating toggle. +- No dark class application. + +## Data Flow + +## Initial Load + +1. Parse cookie `theme`. +2. If cookie is `dark` or `light`, use it. +3. Else query `window.matchMedia('(prefers-color-scheme: dark)')`. +4. Apply result by toggling `document.documentElement.classList` with `dark`. +5. Render page using applied theme classes/variables. + +## User Toggle Interaction + +1. User clicks floating toggle. +2. Determine next theme (`light` -> `dark`, `dark` -> `light`). +3. Update `dark` class on `` immediately. +4. Attempt to write cookie `theme=`. + +Expected outcome: exactly one body include in required position. + +- [ ] **Step 3: Update page shell, vehicle card, and maintenance form in `views/vehicle.php`** + +Concrete updates: +- body/header/card backgrounds `bg-white/bg-gray-*` -> theme classes +- text grays on headings/body -> `theme-text` / `theme-text-muted` +- form inputs/borders -> `theme-input theme-border` + +Expected outcome: top section and add-maintenance form are readable in both themes. + +- [ ] **Step 4: Update dropdown menu surfaces in `views/vehicle.php`** + +Concrete updates: +- suggestions box `bg-white border-gray-300` -> `theme-surface theme-border` +- export menu panel `bg-white` -> `theme-surface` +- export menu links `hover:bg-gray-100` -> `hover:opacity-95` +- export menu link text grays -> `theme-text` + +Expected outcome: dropdown menus remain readable and interactive in both themes. + +- [ ] **Step 5: Update maintenance history surfaces in `views/vehicle.php`** + +Concrete updates: +- history container `bg-white` -> `theme-surface` +- item cards `border-gray-200` -> `theme-border` +- item card hover `hover:border-blue-300` -> `hover:border-blue-500` +- item metadata text gray shades -> `theme-text-muted` + +Expected outcome: maintenance history cards remain readable in both themes. + +- [ ] **Step 6: Update modal panel and input surfaces in `views/vehicle.php`** + +Concrete updates: +- edit modal panels `bg-white` -> `theme-surface` +- modal input `border-gray-300` -> `theme-input theme-border` +- modal close/cancel text grays -> `theme-text-muted` + +Expected outcome: dropdowns, history cards, and modals remain readable and interactive in both themes. + +- [ ] **Step 7: Run syntax check for `views/vehicle.php`** + +Run: `php -l views/vehicle.php` + +Expected: `No syntax errors detected in views/vehicle.php`. + +- [ ] **Step 8: Manual verification for `/vehicles/{id}`** + +Expected: +- theme toggles and persists +- forms/dropdowns/modals remain readable in both themes +- navigation to/from `/home` keeps cookie-based preference + +- [ ] **Step 9: Commit Task 4** + +```bash +git add views/vehicle.php +git commit -m "feat: integrate dark mode support in vehicle detail view" +``` + +### Task 5: Integrate theme partial into settings view + +**Files:** +- Modify: `views/settings.php` +- Test: `/settings` + +- [ ] **Step 1: Add head include in `views/settings.php`** + +```php + +``` + +Placement requirement: after Tailwind/bootstrap includes and before ``. + +Expected outcome: exactly one head include in required position. + +- [ ] **Step 2: Add body include in `views/settings.php`** + +```php + +``` + +Placement requirement: before local scripts (if any) and before ``. + +Expected outcome: exactly one body include in required position. + +- [ ] **Step 3: Update page/header/card surfaces in `views/settings.php`** + +Concrete updates: +- body/header/cards `bg-white/bg-gray-*` -> `theme-page`/`theme-surface`/`theme-surface-alt` +- gray text classes -> `theme-text` / `theme-text-muted` + +Expected outcome: page shell and cards remain readable in both themes. + +- [ ] **Step 4: Update list rows and password form surfaces in `views/settings.php`** + +Concrete updates: +- quick-task row hover/background and borders -> theme classes +- password input borders/backgrounds -> `theme-input theme-border` + +Expected outcome: list rows and form fields are readable and distinguishable in both themes. + +- [ ] **Step 5: Verify action/footer contrast in `views/settings.php`** + +Concrete updates: +- logout action danger colors may be adjusted if needed for dark-mode contrast while preserving danger semantics +- powered footer card `bg-white` -> `theme-surface` +- powered footer title/body grays -> `theme-text` / `theme-text-muted` +- GitHub icon/link muted grays -> `theme-text-muted` while preserving blue link emphasis + +Expected outcome: call-to-action and footer text remain legible in both themes. + +- [ ] **Step 6: Run syntax check for `views/settings.php`** + +Run: `php -l views/settings.php` + +Expected: `No syntax errors detected in views/settings.php`. + +- [ ] **Step 7: Manual verification for `/settings`** + +Expected: +- theme toggles and persists +- cards/list rows/inputs remain readable in both themes +- navigation to/from `/home` keeps cookie-based preference + +- [ ] **Step 8: Commit Task 5** + +```bash +git add views/settings.php +git commit -m "feat: integrate dark mode support in settings view" +``` + +### Task 6: Validate export exception and complete end-to-end checks + +**Files:** +- Verify unchanged: `views/export.php` +- Verify behavior: `views/partials/theme.php` +- Verify integrated views: `views/login.php`, `views/setup.php`, `views/index.php`, `views/vehicle.php`, `views/settings.php` + +- [ ] **Step 1: Verify `views/export.php` has no theme include/toggle** + +Expected: +- no `$themeSection` include +- no `#themeToggle` markup +- no dark-mode bootstrap script and no `html.dark` application path in export template + +- [ ] **Step 2: Verify cookie contract in devtools** + +Flow: +- toggle theme on any themed page +- inspect cookie attributes + +Expected: +- cookie name `theme` +- value `dark` or `light` +- `Max-Age=31536000` +- `Path=/` +- `SameSite=Lax` + +- [ ] **Step 3: Verify accessibility state updates on toggle** + +Flow: +- focus toggle via keyboard +- activate twice + +Expected per click: +- icon swaps moon/sun +- `aria-label` and `title` update to next action + +- [ ] **Step 4: Verify export is always light** + +Flow: +- set `theme=dark` +- open `/vehicles/{id}/export/html` + +Expected: +- export remains light styled +- toggle not present +- `` does not receive `dark` class on export, even when `theme=dark` + +- [ ] **Step 5: Verify toggle stays visible above modals/dropdowns** + +Flow: +- open each page with modal/dropdown support (`/home`, `/vehicles/{id}`) +- open add/edit modals and export/suggestions dropdowns + +Expected: +- floating toggle remains visible and clickable above overlays + +- [ ] **Step 5a: Apply layering remediation if Step 5 fails** + +Flow: +- if toggle is hidden or unclickable over overlays, adjust stacking order in `views/partials/theme.php` + +Concrete remediation: +- increase `#themeToggle` z-index above conflicting overlay layer +- if conflict persists, reduce non-critical overlay z-index where safe without breaking modal usability + +Expected: +- toggle remains visible/clickable while modals/dropdowns still function correctly + +- [ ] **Step 6: Verify no-cookie fallback uses system preference** + +Flow: +- delete `theme` cookie +- load `/home` + +Expected: +- resolved theme follows current `prefers-color-scheme` + +- [ ] **Step 7: Verify invalid-cookie fallback** + +Flow: +- set `theme=invalid` +- reload `/home` + +Expected: +- invalid cookie ignored +- resolved theme follows current system preference + +- [ ] **Step 8: Verify no-JS fallback** + +Flow: +- disable JS in browser +- open `/login` + +Expected: +- light mode only +- no visible floating toggle +- login form remains usable + +- [ ] **Step 9: Verify cookie-write-failure behavior** + +Flow: +- use a browser mode/policy that blocks cookie writes (or simulate via devtools/privacy settings) +- click toggle on `/home` + +Expected: +- theme still switches immediately on the current page +- no error message is shown to the user +- persistence across reload is not required in this scenario + +- [ ] **Step 10: Run final PHP syntax sweep** + +Run: + +```bash +php -l views/partials/theme.php && \ +php -l views/login.php && \ +php -l views/setup.php && \ +php -l views/index.php && \ +php -l views/vehicle.php && \ +php -l views/settings.php +``` + +Expected: all report `No syntax errors detected`. + +- [ ] **Step 11: No additional commit for verification-only task** + +Expected: +- Task 6 records verification results only. +- No new commit is created unless this task introduces actual file changes. diff --git a/docs/superpowers/specs/2026-05-01-dark-mode-design.md b/docs/superpowers/specs/2026-05-01-dark-mode-design.md new file mode 100644 index 0000000..01682c0 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-dark-mode-design.md @@ -0,0 +1,223 @@ +# Dark Mode Support Design + +Date: 2026-05-01 +Project: Mainty +Status: Draft approved by user for implementation planning + +## Goal + +Add dark mode support across all regular app views with a floating dark/light toggle in the top-right corner at all times (for normal JS-enabled operation). Theme should default to the system preference when no user preference exists, and user preference should persist in a cookie. + +## In Scope + +- Add theme resolution and apply logic for: + - `views/index.php` + - `views/vehicle.php` + - `views/settings.php` + - `views/login.php` + - `views/setup.php` +- Add a floating top-right theme toggle visible on all in-scope views. +- Persist user-selected theme in a cookie. +- Apply cookie preference on view load. + +## Out of Scope + +- No database persistence for theme preference. +- No account-level sync across devices. +- No changes to route/controller/model logic. +- No dark mode support for export/print page. + +## Explicit Product Decisions + +- Export page remains always light for print readability: + - `views/export.php` must ignore cookie and system theme. +- If JavaScript is disabled, app falls back to light theme. + +## Architecture + +## 1) Shared Theme Partial + +Create a reusable partial: `views/partials/theme.php`. + +Responsibilities: + +- Inject minimal theme bootstrap script in `
` to resolve theme and apply `dark` class on `` as early as possible. +- Inject floating theme toggle markup near the start/end of `