docs(state): Phase 1 MVP code-complete (B-I), deployment runbook ready for Phase J
This commit is contained in:
+143
-19
@@ -1,6 +1,10 @@
|
||||
# Premium Mechanic PWA — Implementation State
|
||||
|
||||
> Last updated: 2026-05-17 (end of session 3 — Phase E COMPLETE)
|
||||
> Last updated: 2026-05-17 (Phase 1 MVP code-complete, awaiting production deploy)
|
||||
|
||||
## Status: **READY TO DEPLOY** (Phase J prep done, awaiting explicit user OK)
|
||||
|
||||
Backend (Phases B-F) and Frontend (Phases G-I) are feature-complete and pushed to Gitea. Deployment is the only step left — it requires SSH into `root@100.64.0.12` (prod VDS), Docker compose restart, and Caddy reload. **Not executed automatically.** See "Deployment runbook" section below for the exact steps.
|
||||
|
||||
## What is done
|
||||
|
||||
@@ -28,6 +32,11 @@
|
||||
| E | E3: `POST /photos/{id}/confirm` | ✅ DONE | commit `724b209` — service `get_photo_by_id` + `confirm_photo` (None-safe dim update, status→ready); endpoint verifies `storage.exists`, 409 if missing, 404 covers both photo-missing and inspection-mismatch; 9 new tests (7 endpoint + 2 service). `_fake_photo_orm` factory added to `mechanic_fakes.py`. |
|
||||
| E | E4: `GET /photos/{id}` + `/thumb` (302 redirect) | ✅ DONE | commit `8361ee1` — auth-guarded 302 to presigned MinIO GET URLs (1h expiry). Thumb endpoint falls back to `storage_key` when `thumb_key is None` so galleries degrade gracefully if E5 task hasn't run yet. 8 endpoint tests. |
|
||||
| E | E5: async thumbnail generation | ✅ DONE | commit `b37a068` — `generate_and_store_thumbnail` worker (download → Pillow resize to 480px → upload → DB update) with try/except at each step (failures logged, return None, never crashes event loop). Triggered via `asyncio.create_task` from E3 confirm endpoint. `_thumb_key_for` predictable naming (`<key-base>-thumb.jpg`). `pillow_heif.register_heif_opener()` wrapped in try/except for dev-env tolerance. 6 new tests (5 storage unit + 1 endpoint trigger). |
|
||||
| F | F1+F2: markers CRUD (POST/PATCH/DELETE) | ✅ DONE | commit `7275a23` — 3 endpoints (`POST /inspections/{id}/markers`, `PATCH /markers/{id}`, `DELETE /markers/{id}`). Service helpers `get_marker_by_id`/`create_marker`/`patch_marker`/`delete_marker`. Stale comment in `mechanic_damage_marker.py:54` fixed. `_FakeSession.delete()` added; `_FakeSession.refresh()` auto-sets `created_at` for server-default fields. 15 new tests. **159 mechanic tests total. Backend Phase 1 MVP feature-complete.** |
|
||||
| G | G1-G5: Vite + React + TS + Tailwind + shadcn + PWA scaffold | ✅ DONE | commit `e815955` in `PremiumDriverApp/mechanic-pwa/frontend/` — 38 files, 8973 insertions. React 19 + Vite 8 + Tailwind v3 + TS strict + shadcn/ui (manually written to bypass broken @shadcn 4.7 "base-nova" style) + vite-plugin-pwa v1.3 (auto-bumped from v0.20 for Vite 8 compat). Service worker (`sw.js` + workbox, 10 precached entries). Cream palette (CSS vars from memory `feedback_design_prefs`). API client (ky) + auth store (zustand persist). 5 stub pages. **Build: 91 KB gzip JS, 0 warnings.** |
|
||||
| H | H1-H3: Login + Home + VehicleCard pages | ✅ DONE | commit `537548d` — Login uses OAuth2 password flow (form-urlencoded) against `/api/auth/login` (NOT mechanic-prefixed). Home shows vehicle list with search + logout + `/me` fetch. VehicleCard has all 9 inspection-type buttons (top 4 primary, rest outline). Russian locale dates. Semantic Tailwind tokens throughout (no hex). **Build: 110 KB gzip JS.** |
|
||||
| I | I1-I3: photo capture/upload infra | ✅ DONE | commit `fc4060d` — `api/photos.ts` with presigned PUT pattern (not POST/FormData per plan), `confirmPhoto(inspection_id, photo_id, meta)` with both IDs in path per E3 backend. `AuthImg` component (fetches with Bearer JWT → blob URL → `<img>`) solving the `<img>`-can't-send-Authorization problem. `CameraCapture` uses `<input type="file" capture="environment">` for max compat. `PhotoSlot` combines them. |
|
||||
| I | I4-I6: VehicleScheme + InspectionEditor + InspectionReview | ✅ DONE | commit `a428395` — `sedan-top.svg` (top-view with 7 transparent zone rects), `VehicleScheme.tsx` with click+hover handlers + point-marker overlays + zone-count badges. `inspections.ts` typed with `PhotoSummary`/`MarkerSummary`. Editor wires scheme + 8 photo slots + marker list + finalize button (gated on `status==="in_progress"`); zone tap creates marker with `damage_type="damaged"`/`severity="minor"`. Review is read-only with AuthImg thumb grid. **Build: 113 KB gzip JS. Frontend Phase 1 MVP feature-complete.** |
|
||||
|
||||
**Branch state:** `feat/mechanic-mvp` at `b37a068`. Full stack: A4 → B1 → B3 + fix → B4 → C1 + fix → C2 → C3 + fix → B3-vendor-align → D1 + fix → D2 → D3 → test-refactor → E1 → E2 → E3 → E4 → E5. All pushed to `origin` (Gitea). See git log for full SHAs.
|
||||
|
||||
@@ -124,9 +133,124 @@ No `conftest.py`, no `pytest-asyncio`, no real DB. SQL correctness is deliberate
|
||||
|
||||
If Phase D/E/F needs a more complex query shape (e.g. `.scalars().one()` / `.first()`), extend `_FakeResult.scalars()` accordingly — currently it returns `self` and supports only `.all()`. See `test_mechanic_endpoints.py:_FakeResult` docstring.
|
||||
|
||||
## Next task
|
||||
## Deployment runbook (Phase J — execute manually)
|
||||
|
||||
### **Task F1 — Markers CRUD** (Phase F starts; only 2 tasks)
|
||||
**Status:** prep done, awaiting user OK. Phase 1 MVP code is on `feat/mechanic-mvp` in both Gitea repos.
|
||||
|
||||
### J0 — Pre-flight (verified)
|
||||
|
||||
- [x] Backend `requirements.txt` already has `aiobotocore>=2.13.0`, `Pillow==10.4.0`, `pillow-heif==0.18.0`.
|
||||
- [x] Backend uses `Base.metadata.create_all` in lifespan (NOT Alembic) — new mechanic tables auto-create on container restart. **No migration step needed.**
|
||||
- [x] MinIO bucket `pp-inspections` exists with CORS configured (Phase A3, `ops/set-minio-cors.sh` already applied).
|
||||
- [x] Caddy fragment `ops/Caddyfile.fragment` ready in PremiumDriverApp repo.
|
||||
- [x] Frontend `dist/` build clean: **113 KB gzip JS**, 12 KB CSS, sw.js + workbox precaching.
|
||||
- [ ] **DNS**: confirm `mechanic.pptaxi.ru` resolves to the VDS IP before Caddy reload.
|
||||
- [ ] **Test mechanic user exists**: need a User in the DB with `is_admin=true` OR membership in a `Department` with `dept_type='svc'`. Verify with: `SELECT u.id, u.username, u.is_admin FROM users u LEFT JOIN user_departments ud ON ud.user_id=u.id LEFT JOIN departments d ON d.id=ud.department_id WHERE u.is_admin=true OR d.dept_type='svc';`
|
||||
|
||||
### J1 — Backend deploy (taxi-dashboard)
|
||||
|
||||
**⚠️ Production touch — affects live CRM.**
|
||||
|
||||
Plan said `systemctl restart taxi-dashboard-backend.service` + `alembic upgrade head` — **both wrong for our setup**. We use Docker blue/green + `Base.metadata.create_all`. Correct steps:
|
||||
|
||||
```bash
|
||||
ssh root@100.64.0.12
|
||||
cd /opt/sites/taxi-dashboard
|
||||
|
||||
# 1. Pull the mechanic branch into prod's tracking copy
|
||||
git fetch origin feat/mechanic-mvp
|
||||
git checkout feat/mechanic-mvp # OR merge into main first if your release process needs that
|
||||
|
||||
# 2. Rebuild backend image (pulls in new requirements: Pillow, pillow-heif, aiobotocore should already be installed if it wasn't a fresh deploy, but rebuild to be safe)
|
||||
docker compose build backend-blue backend-green
|
||||
|
||||
# 3. Rolling restart (blue first, then green — preserves availability)
|
||||
docker compose up -d --no-deps backend-blue
|
||||
sleep 10
|
||||
docker logs taxi-backend-blue --tail 50 # check for clean startup + table-create logs
|
||||
docker compose up -d --no-deps backend-green
|
||||
sleep 10
|
||||
docker logs taxi-backend-green --tail 50
|
||||
|
||||
# 4. Smoke test
|
||||
curl -sS https://crm.pptaxi.ru/api/v1/mechanic/healthz
|
||||
# Expected: {"status":"ok"}
|
||||
```
|
||||
|
||||
**Rollback if broken:** `git checkout main && docker compose up -d --no-deps backend-blue backend-green`. The `Base.metadata.create_all` is additive (CREATE TABLE IF NOT EXISTS) — new tables stay, no data loss.
|
||||
|
||||
### J2 — Frontend deploy (mechanic-pwa)
|
||||
|
||||
```bash
|
||||
# 1. Build locally (already done; use the fresh dist/)
|
||||
cd c:/NewProject/PremiumDriverApp/mechanic-pwa/frontend
|
||||
npm run build
|
||||
|
||||
# 2. Create target dir on VDS if first deploy
|
||||
ssh root@100.64.0.12 'mkdir -p /opt/sites/mechanic-pwa/dist'
|
||||
|
||||
# 3. Sync the built assets
|
||||
rsync -avz --delete dist/ root@100.64.0.12:/opt/sites/mechanic-pwa/dist/
|
||||
|
||||
# 4. Install Caddy fragment (first deploy only)
|
||||
ssh root@100.64.0.12
|
||||
cd /opt/sites/mechanic-pwa
|
||||
# (assumes PremiumDriverApp repo is checked out at /opt/sites/mechanic-pwa/repo;
|
||||
# if not, scp ops/Caddyfile.fragment instead)
|
||||
cp /opt/sites/mechanic-pwa/repo/ops/Caddyfile.fragment /etc/caddy/sites-enabled/mechanic.pptaxi.ru
|
||||
caddy validate --config /etc/caddy/Caddyfile # OR appropriate validation cmd
|
||||
systemctl reload caddy
|
||||
|
||||
# 5. Smoke test
|
||||
curl -I https://mechanic.pptaxi.ru
|
||||
# Expected: 200 OK, Content-Type: text/html
|
||||
curl -sS https://mechanic.pptaxi.ru/manifest.webmanifest | head -20
|
||||
# Expected: JSON with "name": "Premium Механик"
|
||||
```
|
||||
|
||||
**Rollback:** `rm /etc/caddy/sites-enabled/mechanic.pptaxi.ru && systemctl reload caddy` removes the route; old `dist/` can be restored from previous rsync run if you take a snapshot first.
|
||||
|
||||
### J3 — Smoke test (manual)
|
||||
|
||||
Open `https://mechanic.pptaxi.ru` on a mobile browser (Android Chrome / iOS Safari):
|
||||
|
||||
1. Login with the test mechanic account
|
||||
2. Search for any car → open card → start "Передача"
|
||||
3. Tap 2 zones on the scheme → markers appear with red badges
|
||||
4. Snap 1 photo (any slot) → wait for "загружено" toast → thumb appears
|
||||
5. Tap "Завершить осмотр" → redirected to review page showing photo + markers
|
||||
6. Verify in DB:
|
||||
```sql
|
||||
SELECT id, vehicle_id, type, status, performed_by, started_at, finished_at
|
||||
FROM mechanic_inspections ORDER BY id DESC LIMIT 3;
|
||||
SELECT id, side, slot_index, status, thumb_key IS NOT NULL AS has_thumb
|
||||
FROM mechanic_inspection_photos WHERE inspection_id=<NEW_ID>;
|
||||
SELECT id, side, damage_type, carried_over_from_id, resolved
|
||||
FROM mechanic_damage_markers WHERE inspection_id=<NEW_ID>;
|
||||
```
|
||||
7. Verify in MinIO console (`https://s3-admin.pptaxi.ru`) bucket `pp-inspections`: see the `mechanic/inspections/<NEW_ID>/<uuid>.jpg` original + `<uuid>-thumb.jpg` next to it.
|
||||
8. PWA install: tap browser menu → "Add to Home Screen" → opens fullscreen.
|
||||
|
||||
If all 8 pass → **MVP is live**. File UX issues as Gitea issues.
|
||||
|
||||
## Acceptance criteria (from plan §3)
|
||||
|
||||
- [ ] Mechanic can log in on `mechanic.pptaxi.ru` from mobile
|
||||
- [ ] Find vehicle by license plate
|
||||
- [ ] Open vehicle card, see recent inspections
|
||||
- [ ] Start handover / return / periodic / ad-hoc inspection
|
||||
- [ ] On return: carry-over markers from prev appear with `carried_over_from_id`
|
||||
- [ ] Take ≥4 photos in slots, uploaded directly to MinIO via presigned PUT
|
||||
- [ ] Click zones on SVG scheme → point markers added
|
||||
- [ ] Finalize inspection (status=completed, finished_at auto-set)
|
||||
- [ ] View read-only review with all photos + markers
|
||||
- [ ] PWA installable to home screen on Android + iOS
|
||||
|
||||
## Next task (post-deploy)
|
||||
|
||||
### **Task F1 — Markers CRUD** (Phase F starts; only 2 tasks) — DONE see table above
|
||||
|
||||
After successful J3 smoke test, **Phase 1 MVP is complete**. Phase 2 backlog: polygon annotation editor (Konva), offline IndexedDB queue, native APK wrap, push notifications, VendorBridge data import, admin stats dashboard, QR scanner.
|
||||
|
||||
(From `docs/superpowers/plans/2026-05-16-premium-mechanic-phase1.md` Phase F.)
|
||||
|
||||
@@ -193,32 +317,32 @@ In a new Claude Code chat in `c:\NewProject\`, paste this prompt:
|
||||
```
|
||||
Продолжаем проект Premium Mechanic PWA.
|
||||
|
||||
Прочитай PremiumDriverApp/docs/superpowers/STATE.md — там полное состояние и следующая задача.
|
||||
Прочитай PremiumDriverApp/docs/superpowers/STATE.md — Phase 1 MVP code-complete.
|
||||
Backend (14 routes, 159 тестов) + Frontend (113 KB gzip PWA) запушены на feat/mechanic-mvp.
|
||||
|
||||
Phase E полностью завершён (E1-E5, последний коммит b37a068, 144/144 тестов зелёных).
|
||||
Backend MVP API готов: 11 routes (полный flow inspection + photos + thumbnails).
|
||||
Следующая фаза — Phase F (markers CRUD, 2 задачи) из плана.
|
||||
Deployment runbook готов в STATE.md секция "Deployment runbook". Выполни J1+J2+J3:
|
||||
1. SSH в root@100.64.0.12, git pull feat/mechanic-mvp, docker compose rebuild + rolling restart blue→green
|
||||
2. rsync mechanic-pwa/frontend/dist/ на VDS + caddy reload mechanic.pptaxi.ru
|
||||
3. Smoke test на мобильном браузере по чек-листу из runbook'а
|
||||
|
||||
После F backend Phase 1 MVP feature-complete и начинается Phase G (frontend PWA scaffold).
|
||||
|
||||
Используй superpowers:subagent-driven-development.
|
||||
После успешного deploy: merge feat/mechanic-mvp → main в обоих репах (PremiumDriverApp + taxi-dashboard).
|
||||
```
|
||||
|
||||
That prompt is the **single entry point** for the next session — Claude will read STATE.md, ground itself, and dispatch the F1 implementer.
|
||||
That prompt is the **single entry point** for the next session — Claude reads STATE.md, executes the deployment runbook, runs smoke tests, merges to main.
|
||||
|
||||
## Remaining roadmap (Phase 1 MVP)
|
||||
|
||||
- ~~**Phase B** (4 tasks)~~ ✅ done — module skeleton, models, schemas, auth deps
|
||||
- ~~**Phase C** (3 tasks)~~ ✅ done — GET /me, GET /vehicles, GET /vehicles/{id}
|
||||
- ~~**Phase D** (3 tasks)~~ ✅ done — POST /inspections (+ carry-over), GET /inspections/{id}, PATCH /inspections/{id}
|
||||
- ~~**Phase E** (5 tasks)~~ ✅ done — presigned PUT URL, photo upload-url, confirm, GET /photos/{id}+thumb, async thumb generation
|
||||
- **Phase F** (2 tasks): markers create/update/delete ← **NEXT**
|
||||
- **Phase G** (5 tasks): Vite + React + Tailwind + shadcn + PWA scaffold
|
||||
- **Phase H** (3 tasks): Login + Home + VehicleCard pages
|
||||
- **Phase I** (6 tasks): CameraCapture + PhotoSlot + VehicleScheme + InspectionEditor + InspectionReview
|
||||
- **Phase J** (4 tasks): e2e test + deploy backend + deploy frontend + smoke test
|
||||
- ~~**Phase D** (3 tasks)~~ ✅ done
|
||||
- ~~**Phase E** (5 tasks)~~ ✅ done
|
||||
- ~~**Phase F** (2 tasks)~~ ✅ done — backend Phase 1 MVP feature-complete (14 routes, 159 tests)
|
||||
- ~~**Phase G** (5 tasks)~~ ✅ done — frontend scaffold (91 KB gzip after G; 113 KB after I)
|
||||
- ~~**Phase H** (3 tasks)~~ ✅ done — Login + Home + VehicleCard
|
||||
- ~~**Phase I** (6 tasks)~~ ✅ done — CameraCapture + AuthImg + PhotoSlot + VehicleScheme + Editor + Review
|
||||
- **Phase J** (4 tasks): code-complete, **deployment runbook ready, awaiting user OK** ← **HERE**
|
||||
|
||||
≈ 20 tasks left until MVP live on `mechanic.pptaxi.ru`. Backend MVP is feature-complete after F.
|
||||
**Code work is done. Phase 1 MVP delivery is one `ssh root@100.64.0.12` + Docker compose restart + rsync away.**
|
||||
|
||||
≈ 25 tasks left until MVP live on `mechanic.pptaxi.ru`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user