Development Guide
This guide covers local setup, testing, and coding standards.
Contributing
Contributions welcome — features, fixes, tests, and docs.
- Setup: Follow Quick Start below
- Branch: Create a feature branch from
dev - Code: Make small, focused commits
- Test: Run backend and frontend tests (see Testing)
- PR: Open a pull request to
dev
For detailed git workflow, branching strategy, and conflict resolution, see Git Workflow.
Fast Evaluator Path
If you are opening this repo to assess it quickly rather than contribute to it, use this shorter path:
Run the seeded app
bash./utils/deploy.sh --seedOpen the main surfaces
- App: http://localhost:8000
- Admin: http://localhost:8001
- API Docs: http://localhost:8000/api/documentation
- Architecture: Architecture
Use the seeded accounts
- Admin:
admin@catarchy.space / password - User:
user1@catarchy.space / password
- Admin:
Run the quick review command
bashbun run review:quick
Quick Start
If your shell does not recognize vp, the repo is still usable with bun run ... from frontend/ because those scripts already delegate into Vite+. You can either use that fallback or add your local Vite+ install directory to PATH.
New to the repo? Follow these steps to get running:
Run the app
bash./utils/deploy.shNote: The backend automatically creates
.envfrom.env.examplewhen you run anyphp artisancommand if it doesn't exist. You'll see a helpful message to runphp artisan key:generate.Tip: Use
./utils/deploy.sh --skip-buildfor faster deployments when you've already built the Docker images and just need to restart containers or run database migrations.Access the app
- Main App: http://localhost:8000
- Admin Panel: http://localhost:8001 (admin@catarchy.space / password)
- API Docs: http://localhost:8000/api/documentation
- Project Docs: http://localhost:8000/docs (VitePress)
Admin port settings (
ADMIN_HOST_BIND,ADMIN_HOST_PORT,VITE_ADMIN_URL) belong in the repo-root.env.ADMIN_URLbelongs inbackend/.env.If port 8001 still shows the main app after an upgrade, unregister the stale service worker for
localhost:8001in browser DevTools → Application → Service Workers.Optional: Enable HTTPS for local dev (single compose)
bash# Set in backend/.env APP_ENV=development ENABLE_HTTPS=true # Generate self-signed certificates (one-time setup) # Note: script will NOT overwrite existing certs; use --force to regenerate ./utils/generate-dev-certs.sh # Deploy using the single entry point ./utils/deploy.sh # Access via HTTPS # https://localhost (browser will show security warning - click "Advanced" → "Proceed") # https://localhost:8001 # https://localhost/docsWhy HTTPS in dev?
- Test features requiring secure context (Service Workers, Web Crypto API, etc.)
- Match production behavior more closely
- Test HTTPS-specific security headers
Production note: Production deployments use
docker-compose.ymlonly (no dev override). HTTPS is handled by reverse proxy (nginx/caddy/traefik) with proper certificates.Generate API Client
If you change the backend API (@OA annotations), you must regenerate the frontend types and hooks:
bash# Run from root to sync both backend and frontend vp run api:generateIf
vpis not available in your shell yet, use:bashbun run api:generateAlternatively, run separately:
bashcd backend && php artisan l5-swagger:generate cd ../frontend && vp run api:generateCI/Commit Guardrail To ensure your generated code matches the current backend attributes, run:
bashvp run api:checkShell fallback:
bashbun run api:check(Exits with code 1 if there are uncommitted changes to generated files).
Deployment: The
./utils/deploy.shscript automatically regenerates both the OpenAPI spec and the frontend client during the pre-build stage to ensure the production image is always consistent.Usage: Refer to
frontend/src/api/generated/for the output. Prefer using the generated hooks (useGetPets,usePostPets, etc.) over manual Axios calls for full type safety.See API Conventions for more details on full-stack typesafety.
Test Users (Seeded Data)
- Super Admin: admin@catarchy.space / password
- Admin: user1@catarchy.space / password
- Regular Users: 3 users with factory-generated names/emails / password
Admin Features
- User Impersonation: Click 👤 icon in Users table to impersonate any user
- Stop Impersonating: Use navbar indicator or admin panel to return
- User Ban/Unban: Ban users to put them in read-only mode (view-only, no writes); unban to restore full access
- User Storage Visibility: Open
http://localhost:8001/users/:idto view storage used and storage limit for that user - Storage Limits Config: Open
http://localhost:8001/system-settingsto configure default vs premium storage ceilings
Testing
Backend (Pest/PHPUnit)
Always run backend tests inside the Docker container to ensure proper extensions and PHP version.
Tests run in parallel by default for faster execution.
# Run all tests (parallel by default)
cd backend && php artisan test --parallel
# Run tests without parallel execution
cd backend && php artisan test --no-parallel
# Run specific test suites
cd backend && php artisan test --parallel --testsuite=Feature
cd backend && php artisan test --parallel --testsuite=UnitRunning tests locally (outside Docker):
If you need to run tests on your local machine:
cd backend
# First time only - .env will be auto-created from .env.example
composer install
php artisan key:generate
# Configure your local database settings in .env, then run tests
php artisan test --parallelFrontend (Vitest)
cd frontend
# Run all tests
vp test
# Interactive UI
vp run test:ui
# Coverage report
vp run test:coverageEnd-to-End (Playwright)
Playwright E2E tests live under frontend/e2e/.
For this project, the default E2E runner (frontend/scripts/e2e-test.sh) manages Docker services (db, backend, mailhog) and database seeding automatically before running Playwright.
Offline-mode E2E coverage lives in frontend/e2e/offline-mode.spec.ts (pet create/edit/delete, medical record create, and habit day check-in queue/replay). Integration coverage for the same journeys without a full browser is in frontend/src/offline/offline-mode.integration.test.tsx. See Offline Mode for the feature matrix and test-layer guidance.
The checked-in Playwright config now defaults to a single worker for stability with the seeded shared accounts. Override it with PLAYWRIGHT_WORKERS only when you intentionally want to trade some determinism for speed.
Quick start:
# From frontend/
cd frontend
vp run e2e # headless run
vp run e2e:ui # interactive UI
vp run e2e:report # open last HTML reportImportant when debugging against http://localhost:8000: Playwright is exercising the Docker-served app, not a live Vite dev server. If you change frontend code that is bundled into the backend-served SPA, rebuild the frontend and then rebuild the backend container image so the running app actually serves the new assets:
cd frontend
bun run build
cd ..
docker compose up -d --build backendA plain docker compose restart backend is enough for backend-only PHP changes, but not for frontend asset changes that are baked into the image.
If Playwright browsers are missing on your machine, install them once:
cd frontend
vp exec playwright install chromiumStatic Analysis & Quality Gates
Code Quality (Formatting)
Run Laravel Pint to automatically format code according to PSR-12 and Laravel conventions.
# From the backend directory
cd backend
./vendor/bin/pintPHPStan (Backend Type Analysis)
PHPStan enforces type safety and catches bugs at write-time using static analysis. Currently configured at Level 5.
Run analysis:
cd backend
composer phpstanDeptrac (Architecture Layer Enforcement)
Deptrac prevents architectural violations by enforcing allowed dependencies between layers.
Run analysis:
cd backend
composer deptracAsset Management
PWA Assets
PWA icons, screenshots, and manifests are maintained as explicit source assets in frontend/public and mirrored to backend/public when they need to be served from Laravel's public root. Do not regenerate the web manifests from an icon-only script: the manifests include install metadata such as id, screenshots, shortcuts, categories, theme colors, and the offline start URL.
Root-level icons such as favicon.ico, apple-touch-icon.png, and icon-32.png are served with a 1-day browser cache in the backend container NGINX config. After updating branding assets, expect clients to pick them up automatically within a day unless you also change the filenames.
The frontend ships separate light and dark web manifests. Theme switching between them is handled by the app-owned runtime in frontend/src/lib/theme-runtime.ts, while the backend SPA shell applies the initial resolved theme before hydration so the manifest, theme-color, and color-scheme metadata stay in sync from first paint onward. Those manifests intentionally do not lock orientation, so the installed PWA can rotate with the device.