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>
194 lines
9.9 KiB
Markdown
194 lines
9.9 KiB
Markdown
# 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 |
|