Aug 7 - Update: documents, no code update
This commit is contained in:
@@ -812,7 +812,7 @@ limiter = Limiter(
|
||||
|
||||
## 17. Alembic Migration Chain
|
||||
|
||||
**Current HEAD:** `phase40_support_chat_kb` (38 migrations total).
|
||||
**Current HEAD:** `phase52_user_ui_theme` (51 migrations total).
|
||||
|
||||
**Chain root:** `0003_add_user_active` — a guarded squashed baseline (MT-2) that recreates the full 25-table schema with INFORMATION_SCHEMA guards. The original baseline migrations (0001/0002/0003) were lost; this file restores the chain root so Alembic can build the revision map. `down_revision = None`.
|
||||
|
||||
@@ -846,9 +846,31 @@ limiter = Limiter(
|
||||
→ phase36_issue_work_orders
|
||||
→ phase37_contract_recipients
|
||||
→ phase38_facility_qr
|
||||
→ phase39_issue_handler_type → phase40_support_chat_kb ← HEAD
|
||||
→ phase39_issue_handler_type → phase40_support_chat_kb
|
||||
→ phase41_auditor_role → phase42_area_qr_token → phase43_schedule_plan_fields
|
||||
→ phase44_internal_handler → phase45_schedule_frequency_enum
|
||||
→ phase46_schedule_recurrence → phase47_schedule_end_date
|
||||
→ phase48_schedule_parent_inspection → phase49_followup_requested_by
|
||||
→ phase50_sched_acknowledged → phase51_external_inspector
|
||||
→ phase52_user_ui_theme ← HEAD
|
||||
```
|
||||
|
||||
`phase41` → `phase52` are the single-tenant feature-parity track — see
|
||||
MULTI_TENANT_PLAN.md §12. Two notes on that tail:
|
||||
|
||||
* **`phase51_external_inspector`** widens the `users.role` ENUM. It must be
|
||||
deployed together with its code: MT tested `role == 'inspector'` literally in
|
||||
~80 places, and widening the ENUM alone drops external inspectors into the
|
||||
*unscoped* branch, which is a cross-tenant data leak rather than a cosmetic
|
||||
bug. Use `User.INSPECTOR_ROLES` / `user.is_inspector`, never a literal.
|
||||
* **ST's `phase50_default_modern` is deliberately NOT ported.** It overwrites
|
||||
every saved `ui_theme` preference, which in MT would run against every tenant
|
||||
DB. Set `DEFAULT_UI_THEME=modern` per tenant instead. See
|
||||
MULTI_TENANT_PLAN.md §12.3.
|
||||
|
||||
Migration revision IDs must be **≤ 32 characters** to fit
|
||||
`alembic_version.version_num VARCHAR(32)`.
|
||||
|
||||
### phase40_support_chat_kb
|
||||
|
||||
Creates three tables backing support chat persistence and the AI knowledge base:
|
||||
@@ -1734,7 +1756,57 @@ Row highlights: yellow = suspended, red = trial expired.
|
||||
|
||||
---
|
||||
|
||||
## 26. Coding Rules for AI Assistants
|
||||
## 26. Web Portal Design (MT-16)
|
||||
|
||||
Two designs share one set of page templates.
|
||||
|
||||
* `base.html` is a **one-line dispatcher**: `{% extends jqc_layout %}`. Page
|
||||
templates keep `{% extends "base.html" %}` and need no edits.
|
||||
* `layouts/classic.html` is the original chrome, verbatim.
|
||||
`layouts/modern.html` is the sidebar shell.
|
||||
* `jqc_layout` comes from `inject_ui_theme()` in `app/__init__.py`, driven by
|
||||
`users.ui_theme` with config `DEFAULT_UI_THEME` as the fallback.
|
||||
* Per-page overrides live at `templates/modern/<same path>.html` and are indexed
|
||||
once at boot. Look for `UI themes | modern overrides indexed: N` in the log —
|
||||
`0` on a host that should have them means the directory did not deploy.
|
||||
|
||||
**Do not move the template swap into the Jinja loader.** It lives in
|
||||
`ThemedEnvironment.get_template()` so the template cache is keyed on the
|
||||
*rewritten* name. A loader-level swap caches under the original name, so a
|
||||
modern template can be served to a classic user — and in MT, where one worker
|
||||
serves many tenants, across tenants.
|
||||
|
||||
When adding a page: write it once as a normal template. Only add a
|
||||
`modern/` override if the layout genuinely differs; styling alone is handled by
|
||||
`static/css/theme_modern.css`, which is scoped to `body.jqc-modern`.
|
||||
|
||||
---
|
||||
|
||||
## 27. Roles (MT-15)
|
||||
|
||||
`users.role` ENUM: `admin`, `director`, `inspector`, `external_inspector`,
|
||||
`project_manager`, `customer`, `auditor`.
|
||||
|
||||
**Never test `role == 'inspector'`.** `external_inspector` (customer /
|
||||
third-party inspectors) has identical capabilities and identical
|
||||
`InspectorAssignment` scoping. Use:
|
||||
|
||||
* `user.is_inspector` — true for both inspector roles; use for every capability
|
||||
and scoping check
|
||||
* `User.INSPECTOR_ROLES` — for `User.role.in_(...)` queries
|
||||
* `user.is_external_inspector` — only where the two genuinely differ (display)
|
||||
* `user.role_label` / `ROLE_LABELS` — for any role name shown in the UI
|
||||
|
||||
A literal comparison sends external inspectors down the unscoped branch, where
|
||||
`get_inspector_scope()` returns `None` and every downstream query drops its
|
||||
facility filter. That is a cross-customer leak.
|
||||
|
||||
External inspectors are **invited**, never given a password: `password_set=False`
|
||||
plus an emailed 72-hour token, with `auth.resend_invite` for bounced invitations.
|
||||
|
||||
---
|
||||
|
||||
## 28. Coding Rules for AI Assistants
|
||||
|
||||
These rules apply to every change made to this codebase, without exception.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user