Files
tehran_taj/docs/parity/PROTECTED.md
T
s.esmaeiliandClaude Opus 5 71192ba2ba parity(docs): Phase 0 inventory, mapping and draft PROTECTED list
Read-only inventory of all 7 projects (3 reference + 4 target) produced by
parallel subagents, every claim cited path:line. No source code changed.

- 00-INVENTORY.md  consolidated mapping: stack, backend, admin, user apps
- PROTECTED.md     draft protected list (awaiting user confirmation)
- ISSUES.md        26 logged issues (security, money/stock, validation, dead code)
- CHANGELOG.md     change log scaffold
- PROGRESS.md      resumable state + Q1-Q5 blocking Phase 1
- _inv-*.md        7 raw per-project inventories (8111 lines)

Key finding: the reference is a gold-jewellery B2B/B2C e-commerce system, not a
gold bank. Buy/sell, deposit/withdraw, transfer, KYC levels, payment callbacks,
cron and websockets do not exist in it, so they cannot be parity gaps.

[ref: henza_server, henza_admin, henza_pwa]

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 18:09:46 +03:30

194 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROTECTED — intentional monorepo additions (additive-only)
**DRAFT — awaiting user confirmation (Phase 0 gate).**
Items here are intentional changes in `gold-banking-monorepo`. Per `CLAUDE.md` §4
they must **not** be removed, restyled, restructured, or have their behaviour
changed. You may only **ADD** what the reference has and they lack, in a way that
doesn't break them. Any fix that touches a protected item needs explicit approval
with a diff plan first.
Legend: **[brief]** = named as protected in the kickoff brief · **[found]** =
discovered in Phase 0, proposed for protection · **[?]** = needs your ruling.
---
## A. Admin panel — Dashboard · **[brief]**
**Location:** `apps/admin-panel/src/views/Dashboard.vue` (327 lines) — one file
serving both `/dashboard/ecommerce` and `/dashboard/analytics`.
**Gate:** rid `dashboard.view`; lacking it redirects to `my-data` (not a 403 page).
**Data:** a single `GET admin/dashboard/overview` on mount, then client-side sorts
(`reagent` desc by `grams_sell`; `lastOrder` desc by `w`).
Contents, in render order:
1. 4 KPI tiles — مشتریان جدید (نفر) · طلای فروش‌رفته (گرم) · تعداد سفارشات · نرخ هر گرم طلای ۱۸ عیار
2. 2 KPI tiles — سفارشات تحویل‌شده · مجموع بدهی طلای مشتریان (گرم)
3. Area chart «روند سفارش‌ها» — 2 series (تک / عمده); x-labels rotated **-45°** with
`rotateAlways` + `hideOverlappingLabels` (a deliberate overlap fix — do not "tidy" it)
4. 2 donuts — وضعیت سفارشات (8-entry colour array) · تفکیک عمده/تک
5. Horizontal bar + an always-rendered agent table (کارشناس فروش / میزان فروش گرم / تعداد سفارش)
6. Vertical bar «محصولات پرفروش» with an `a-empty` fallback
7. `a-descriptions` «میزان فروش» — 3 columns incl. a computed average
8. Table «سفارشات هفته جاری» — `pageSize` 10, `sts_str` as a coloured `a-tag`, `caj` via `jdate`
Fixed palette `#1c2d56 / #28c76f / #ff9f43 / #ea5455`. **No filters, no date range,
no export, no refresh** — that is the intended design, not a gap.
> Why this matters for parity: the reference's two dashboards
> (`DashboardECommerce.vue`, `DashboardAnalytics.vue`) were found in the earlier
> port to be **mostly commented-out Vuexy demo markup**, with ApexCharts imported
> but never used in live markup (`docs/CHANGELOG-PORT-HISTORY.md`). So there is
> essentially nothing to port *into* this dashboard. Phase 1 will confirm and
> record that, and will not touch this file.
---
## B. Admin panel — the extra option in product Variants · **[brief]**
**Location:** `apps/admin-panel/src/views/apps/product/AddProduct.vue:151-270,
315-361, 519-568` (Create/Edit Product).
**Gate:** the card is gated `product.list.variants`; add/edit/remove on
`.add` / `.edit` / `.remove`.
Variant table columns: تصویر | عنوان | وزن | اجرت درصدی | تعداد | فعال (tag) |
« پیش سفارش» (tag) | عملیات.
**The protected extra field is `تصویر تنوع` (variant image).** It is a **picker,
not an uploader**: a 64×64 flex grid over the product's already-saved gallery
(`data.gallery`, each `{id, url, role, sortOrder}`), whose first tile is a
`بدون تصویر` option and whose selected tile gets a blue `.is-on` border.
`pickVariantImage()` sets `image_id` (posted) plus `image_url` (thumbnail only,
stripped before POST).
It is populated **only in edit mode** — on create the gallery is empty and an
`a-alert` shows a context-dependent message (`ابتدا محصول را ذخیره کنید…` vs
`برای این محصول تصویری ثبت نشده است.`).
Variants are edited **purely in memory** (row key `__key = v-<ts>-<rand>`) and
saved with the product via `PUT product/update/{id}` / `POST product/create` under
a `variants[]` array — there is **no per-variant endpoint**. Only validation is
`عنوان تنوع الزامی است`; delete has **no confirm dialog**.
> Related, and NOT protected by itself: the same `imageId`/`image` contract on the
> **user-app** side. `user-app` consumes it; `user-app-v2` does **not** (gap V2-03).
> Closing that gap in v2 is additive and does not touch this admin code.
---
## C. User apps — new designs and new features · **[brief]**
Both `apps/user-app` and `apps/user-app-v2` keep their own visual design. The
following were identified in Phase 0 as design/feature additions with no
`henza_pwa` equivalent.
### C.1 Shared by both apps
| Item | Location |
|---|---|
| `/notifications` route + screen (self-documented "no legacy equivalent") | `views/user/NotificationsView.vue` |
| `ui/PageBar.vue` replacing the legacy `AppHeader` | `components/ui/` |
| Redesigned CSS bottom nav (5 tabs; legacy was a 4-tab GSAP notch) | `layout/` |
| `GallerySlider.vue` rewrite + teleported full-screen image preview | `components/product/` |
| Variant chips with per-variant images, stock badges, «نمایش همه» | `components/product/SingleProduct.vue` |
| The whole stock / pre-order rule (`qty`, `isPreOrder`, `capQty`, `maxQty`) | `stores/cartStore.ts`, `composables/useProduct.ts` — **v1 only; porting to v2 is gap MON-01** |
| vant pull-to-refresh on 5 screens | various views |
| IntersectionObserver incremental rendering (20-item window) | list views |
| Ledger-based «اسناد» screen | `views/user/TransactionsView.vue` |
| Order-detail drawer + 4-step tracker + `useOrderItemProduct` join | `views/user/OrdersView.vue` |
| 9-chip `OrderStatus` enum | `types/` |
| Dotlottie update dialog | app shell |
| New API contracts: `GET /home/slider`, `GoldPriceQuote`, `productCode`, `collections[]`, `gold-bank:*` session keys | `api/client.ts` |
### C.2 `user-app` (design v1) only
Home family: `components/home/RateStrip.vue`, `ShowcaseSlider.vue`,
`ShowcaseCard.vue`, `CategoryRail.vue`, `SectionHead.vue`, `AccountStrip.vue`;
admin banner via `GET /home/slider` + `AdminSlide`; the «برگزیده منتزر» and
«تازه‌ها» home sections; trust-badge rows.
### C.3 `user-app-v2` (design v2) only
`components/home/HeroCarousel.vue`, `ProductCarousel.vue`, `CategoryGrid.vue`,
`ProductPanel.vue`; the IRANSans FaNum font family (12 files,
`src/assets/styles/fonts/iransans/`); redesigned HomeView structure; redesigned
DashboardView (hero card + 4 settings rows, replacing card + tile grid); the
staggered first-paint reveal system (module-scope `hasRevealed`) in
HomeView/CartView/DashboardView; the `compact` RateStrip pill variant;
SingleProduct's scroll-driven solid header + category deep-link pill; the
pill-shaped SearchInput.
> **Important boundary.** C.2 and C.3 are *design* differences and stay separate.
> The five items in `ISSUES.md` §V2-01..05 are *logic* differences and are **not**
> protected — `CLAUDE.md` §2 requires logic to be identical across both apps.
---
## D. Backend — stricter auth than the reference · **[?] needs your ruling**
**Not yet protected — Q1 in the gate.**
The reference leaves every admin route unauthenticated (`henza_server/routes/admin.php:12`
and `:272` — the `auth:api` group wrapper is commented out at both ends, verified).
`apps/api` instead applies a global `JwtAuthGuard` + `PermissionsGuard` with
144 server-enforced rids, and only 15 routes opt out via `@Public()`.
Strict parity would mean **removing** authentication from ~215 admin endpoints.
`CLAUDE.md` §3 already carves out exactly this case ("if matching the reference
would cause … a clear security hole in money flows → STOP and ask").
**Proposed:** protect the target's stricter auth as an accepted deviation; log the
reference behaviour as `SEC-01` and the residual `@Public()` routes as `SEC-02`.
---
## E. Backend — Decimal money discipline · **[found] proposed**
The reference uses PHP floats throughout. `apps/api` has **zero `Float` columns**
in `prisma/schema.prisma`; ledger and order math run on `Prisma.Decimal`.
Reverting to floats for "parity" would introduce rounding error into money and
gram values. **Proposed:** protect the Decimal discipline. The one leak
(`GRAMS_PER_MESGHAL` as a JS float, `ledger.service.ts:189`) is logged as `MON-03`,
to be fixed in Phase 3 — not now.
Note this does **not** exempt the target from matching the reference's *arithmetic*,
including the `floor(price/1000)*1000` rounding in `ProductVariant.php:183-190`.
Same result, better representation.
---
## F. Prior deliberate non-ports · **[?] needs your ruling (Q4)**
Recorded in `docs/CHANGELOG-PORT-HISTORY.md` as intentionally **not** ported:
1. Unauthenticated file upload (`file/create` had no middleware in the reference)
2. SMS sent inside `toggleActive`
3. Six destructive data behaviours — deleting a confirmed document, editing a
confirmed amount, deleting a category that has dependents, creating a category
cycle, orphaning the audit trail — each replaced with a Persian 400 message
4. The reference's avatar-load crash (`ReagentCustomer.vue:280` dereferenced
`data.image.id` unguarded)
5. The branch-mode horizontal layout (`HorizontalNavMenu`, 4 files never ported;
`mainLayoutType` narrowed to `'vertical'`)
Items 1–4 are security/data-loss carve-outs consistent with `CLAUDE.md` §3 and are
**proposed for protection**. **Item 5 is different** — it is a missing feature, not
a safety carve-out, and Phase 1 should treat it as a real gap unless you say
otherwise.
---
## Summary — what needs your decision
| Section | Item | Proposed |
|---|---|---|
| A | Admin Dashboard | protect (per brief) |
| B | Product Variants image picker | protect (per brief) |
| C | User-app designs + new features | protect (per brief), **excluding** the 5 v1↔v2 logic gaps |
| D | Stricter admin auth in `apps/api` | protect — **confirm (Q1)** |
| E | Decimal money types | protect — **confirm** |
| F.1–4 | Prior security / data-loss non-ports | protect — **confirm (Q4)** |
| F.5 | Branch horizontal layout | **treat as a real gap** — confirm |