Remove DialogVant/NotifyVant and add Toast and Sheet components with
useToast, useReveal, and usePageTransition composables. Rework
Navigation, RegisterView, and styles across both apps; rebuild dist assets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports "تکمیل پروفایل" end to end (D22).
API
- POST customer/me/complete-profile — three required document ids, the
customer taken from the token, verification step set to the reference's
LIVE value (approved outright; its `= 1` line is commented out at :83 in
favour of `= 2` at :85). Both SMS messages byte-identical and non-blocking
per D20.
- POST customer/me/documents — the reference let the app upload through the
same file/create the admin used, inside its auth:api-customer group
(routes/api.php:95); the target's copy is admin-guarded, so the page could
not have uploaded at all. Reuses AdminUploadsService unchanged, so both
paths share one size limit, MIME whitelist and naming scheme (UPL-01).
- PROFILE_ADMIN_MOBILES replaces the reference's four hardcoded personal
numbers; blank means nobody is notified and the upload still succeeds.
Three reference behaviours deliberately NOT ported, all in ISSUES.md:
- SEC-14: `$customer->update($request->all())` mass-assigned the whole request
body, letting the app write its own credit limit, making charge, gold debt,
active flag or mobile. Replaced with a three-field whitelist.
- SEC-15: four real personal mobiles hardcoded in the source.
- APP-03: refresh()'s unterminated setInterval page reload.
Apps (both, template+script byte-identical, per-app styling only)
- CompleteProfileView + SingleImageUploader
- route /complete_profile, redirecting home once the documents are in, as the
reference's guard did (router/index.ts:266)
- the entry point on the profile screen, shown only while they are missing
(profile.vue:96-102)
Also (D23): JWT_CUSTOMER_SECRET now warns loudly at boot when unset. It falls
back to JWT_SECRET, which signs app and admin tokens with the same key and
leaves the `typ` check as the only barrier — and an expert's app token carries
their AdminUser id in `sub`. Warn rather than refuse to boot (D19, lowest
risk); DELIVERY.md lists it as the first pre-release step (REL-03).
Verified: build 5/5 · jest 45/45 · complete-profile 38/38 (new) ·
expert login 36/36 · apps smoke 43/43 · admin smoke 43/43 ·
B2B scan 34 ok/0 broken · typecheck clean on all four apps.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
D20: OTP_MASTER_CODE login bypass read only from the environment, disabled when
unset, audited on every use, with a boot banner while armed. SMS no longer
aborts the boot in production — it warns and declines silently, so no message
can stop an operation.
D19: port Utility::faTOen onto admin-entered mobiles (before the uniqueness
lookup, which could previously be bypassed with Persian digits), and the
create-only welcome SMS, byte-identical to the reference and fire-and-forget.
user-app-v2 was missing the cart stock ceiling entirely: capQty/maxQty, the
un-orderable guard and the qty clamp are ported, leaving v2's own design intact.
The two cart stores are now byte-identical. isVariantOrderable and the
orderable-first default selection ported too.
jest 45/45; typecheck clean on api, admin-panel, user-app, user-app-v2.
[ref: henza_server/app/Http/Controllers/Adm/AdminController.php:333,348,364,378-387]
[ref: henza_server/app/Models/Utility.php:26]
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Extract product image gallery into a dedicated GallerySlider component
with swipe/tap support
- Add a teleported fullscreen image preview (tap to open, thumbnail strip,
body scroll lock) to SingleProduct
- Restore the Trust section that was accidentally commented out
- Swap back/favorite button positions and recenter the image counter
- Fix Icon.vue: tempIcon was a static ref set once at setup, so it never
updated on prop change when Vue reused the component instance (e.g. cart
qty steppers toggling trash/minus icons); switched to computed + watch
- Rename brand text from "منتزر" to "هنزاگلد"
- Fix a CSS typo in user-app-v2 SingleProduct (rgba alpha value)
- Rebuild dist assets
DashboardView ("میز کار"): Rebuilt from scratch with a new structure, distinct from the old card+grid layout but visually aligned with HomeView (same blurred bar, @ink/@wine palette, staged reveal animation on mount):
Single hero card (ink→wine gradient) showing balance, gold/fiat debt, and identity — no quick-action icons inside it anymore.
Live gold rate folded into a slim inline row at the bottom instead of a titled section.
Iterated shadow/spacing on the hero card per feedback (softer, less clipped).
DialogVant / DialogVantOneButton (shared modal components, used in 7+ call sites: Dashboard, Cart, SingleProduct, Register, App): Replaced Vant's default dialog chrome with the app's own rounded-card language — circular icon badge for the illustration, pill-shaped primary/secondary buttons. No call-site markup changed.
LoginView: Rebuilt from scratch — full-bleed dark hero (brand + step copy) overlapped by a floating white form sheet, animated crossfade transition between the mobile-number and OTP steps, same staged entrance animation as Home/Dashboard. All auth logic (API check, OTP stub, session login) unchanged.
- Fix silent redirect-to-home for unauthenticated users on protected routes;
now redirects to login with a return path
- Restyle OrderCard, SearchInput, Filters, DropDown, and product card image
sizing/centering to match the app's LESS-token design language
- Fix ant-design Slider handle placement/drag under RTL and seed a valid
default range so handles actually render
- Fix chip-row edge padding and top spacing across Orders/Cart/Search lists
- Add pull-to-refresh to the dashboard and fix its header/padding to match
the fixed PageBar pattern
- Remove the floating WhatsApp button from index.html
Phase 2, completing the app-wide rollout of the home/product-detail design
language (LESS colour tokens, BEM class names, hairline-bordered cards,
SectionHead, dashed empty states) onto the account side: dashboard, orders,
transactions, profile, login and register.
Reuse over re-implementation: the dashboard's live rate block is now
components/home/RateStrip.vue rather than a second hand-built strip, and
every screen uses components/ui/PageBar.vue for its chrome.
OrderCard and OrderItemCard are restyled to match. OrderItemCard drops its
always-empty note row: our OrderItem schema has no note field (legacy's
per-item note was a real column there; ours is not), so it could only ever
print a dash.
OrdersView swaps ant Tabs for the same scrolling chip rail the rest of the
redesign uses, and its full-screen Modal with hand-written CSS overrides for
a bottom Drawer, matching how every other detail surface opens.
Deleted AppHeader.vue, orphaned once every screen moved to PageBar. Its
shutter animation was driven by transitionFlag, which nothing reads any
more — the router afterEach comment claiming otherwise is corrected rather
than left asserting something untrue, and the now-inert half is flagged in
place. ScrollFetchSearch loses its isFav prop: it gated two empty-state
branches that rendered the identical image, and its only caller
(FavoritesView) now renders its own.
Behaviour is preserved throughout. Orders still reads GET
/customers/:id/orders and filters client-side; transactions still reads
GET /customers/:id/ledger-entries with legacy's CREDIT/DEBIT colour meaning
intact; profile stays read-only over real fields; login keeps the real
customer lookup plus the documented OTP stub; register stays stubbed with
copy that does not claim an account was created.
Verified in a real browser against the running API and native database:
dashboard 4 tiles with a real balance, orders 1 real order card, profile 4
real spec rows, transactions the correct empty state for a customer with no
ledger rows, and login/register confirmed with the session cleared (the
guard bounces a signed-in user away from /login, which is why they needed a
separate pass). No console errors, no horizontal overflow. vue-tsc and vite
build both clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Applies the home/product-detail design language (LESS colour tokens, BEM
class names, hairline-bordered cards, SectionHead, dashed empty states) to
the screens a shopper actually walks through: search, collection, newest,
cart and favorites.
Two shared pieces are extracted so the language stays consistent rather than
re-typed per screen:
- components/ui/PageBar.vue — the sticky translucent bar with blur and a
hairline, lifted from the home screen's own header so every other screen
gets identical chrome instead of the old dark AppHeader.
- components/product/ProductCard.vue — the catalogue workhorse card. It is
deliberately a sibling of home/ShowcaseCard (which keeps its editorial "wide"
variant and badge) and additionally surfaces stock, which the
catalogue needs and the home screen does not.
Deleted as orphaned once nothing imported them: ProductList,
ProductVerticalFull, ProductVertical, ProductHorizontal, CollectionCard —
all Tailwind utility soup carried over from the legacy PWA.
Behaviour is preserved throughout; only chrome and cards change. The cart
still posts to the real POST /orders with wagePercent from each item's
pinned variant, still gates on a real session, and still keeps the note
drawer and success step. Search keeps its client-side filtering, the Filters
sheet, category chips and collection tiles. No rial price is printed
anywhere, for the same reason as the rest of the redesign: a finished
piece's price is the live gold rate x weight plus wage, computed at order
time and never stored on the product.
FIXED a regression this redesign introduced before it shipped: dropping
ScrollFetchSearch from the search grid meant all 205 catalogue products
mounted at once (205 cards, 205 images). Both search and newest now render
a 20-item window that grows as the user reaches the end.
Verified in a real browser against the running API and native database, not
just a green build: home 13 cards, search 20 -> 40 -> 60 -> 80 on scroll,
newest 20 -> 40 -> 60, cart empty state, favorites 3 cards behind a seeded
session. All images loaded (0 broken), no horizontal overflow, no console
errors beyond pre-existing vue3-lazyload noise that predates this work.
vue-tsc and vite build both clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Design language introduced here and reused across both screens:
- LESS colour tokens (@ink/@inkSoft/@inkMuted/@inkFaint, @wine/@wineSoft,
@canvas/@canvasSoft, @hairline/@hairlineSoft) added to
themeConfig/colors.json and injected globally via vite's modifyVars, so
scoped <style lang="less"> blocks resolve them with no import
- BEM-ish scoped class names, hairline-bordered cards at 14-18px radii,
ink surfaces with a wine accent, dashed-border empty states
- components/home/: SectionHead, ShowcaseCard, ShowcaseSlider, CategoryRail,
RateStrip, AccountStrip
Data honesty is deliberate throughout: no rial price is printed for a piece,
because a finished item's price is the live gold rate x weight plus wage,
computed at order time and never stored on the product. Only genuinely
present fields are shown (weight, making-charge percent, stock, category,
brand). Categories and the hero are derived from the real catalogue, so an
empty catalogue renders an empty state rather than placeholder tiles.
Also fixes a real bug in the product detail view: it ran, at module scope,
history.pushState + window.onpopstate = () => history.go(1). Because the
drawer mounts its slot eagerly, that fired for every product card on the
home screen and permanently hijacked the back button for the session.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>