From cda5d21d0d254ba4cb140bae1b0454396556767b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=B4ng=20=C4=90=E1=BB=A9c=20Huy?= Date: Thu, 13 Aug 2026 14:23:44 +0700 Subject: [PATCH] Stage M8 --- README.md | 13 +- .../20260813063631_add_wishlist/migration.sql | 23 + apps/api/prisma/schema.prisma | 27 +- apps/api/src/modules/auth/auth.controller.ts | 22 +- apps/api/src/modules/auth/auth.module.ts | 3 +- apps/api/src/modules/auth/auth.service.ts | 68 ++- .../modules/auth/guards/access-token.guard.ts | 59 +- .../modules/checkout/checkout.controller.ts | 27 +- .../src/modules/checkout/checkout.module.ts | 3 +- .../src/modules/checkout/checkout.service.ts | 12 +- .../modules/customers/customers.controller.ts | 115 ++++ .../src/modules/customers/customers.module.ts | 27 +- .../modules/customers/customers.repository.ts | 171 ++++++ .../modules/customers/customers.service.ts | 282 +++++++++ .../api/src/modules/customers/public/index.ts | 9 +- .../modules/orders/my-orders.controller.ts | 64 ++ apps/api/src/modules/orders/orders.module.ts | 6 +- .../src/modules/orders/orders.repository.ts | 3 + apps/api/src/modules/orders/orders.service.ts | 40 ++ apps/api/src/modules/users/users.service.ts | 50 ++ .../(account)/account/addresses/page.tsx | 15 +- .../app/[locale]/(account)/account/layout.tsx | 61 +- .../(account)/account/orders/page.tsx | 15 +- .../app/[locale]/(account)/account/page.tsx | 15 +- .../(account)/account/profile/page.tsx | 15 +- .../(account)/account/wishlist/page.tsx | 15 +- .../src/app/[locale]/(account)/layout.tsx | 36 ++ .../src/app/[locale]/(account)/login/page.tsx | 29 + .../app/[locale]/(account)/register/page.tsx | 29 + apps/storefront/src/app/[locale]/layout.tsx | 5 +- .../src/components/commerce/checkout-form.tsx | 52 +- .../components/commerce/product-detail.tsx | 3 + .../src/features/account/account-panels.tsx | 559 ++++++++++++++++++ .../src/features/account/account-shell.tsx | 98 +++ .../src/features/account/auth-form.tsx | 188 ++++++ .../features/account/customer-provider.tsx | 124 ++++ .../src/features/account/wishlist-button.tsx | 100 ++++ apps/storefront/src/lib/api.ts | 64 +- apps/storefront/src/lib/routes.ts | 2 + apps/storefront/src/messages/en.json | 64 +- apps/storefront/src/messages/vi.json | 64 +- ...pt-guest-orders-and-never-gate-checkout.md | 103 ++++ docs/adr/README.md | 1 + packages/api-client/src/create-client.ts | 3 + packages/api-client/src/index.ts | 3 +- packages/api-client/src/resources/account.ts | 89 +++ packages/api-client/src/resources/auth.ts | 12 + packages/api-client/src/resources/catalog.ts | 1 + packages/types/src/customers/account.ts | 28 + packages/types/src/index.ts | 1 + packages/validation/src/catalog.ts | 12 + packages/validation/src/customers.ts | 42 ++ packages/validation/src/index.ts | 1 + 53 files changed, 2711 insertions(+), 162 deletions(-) create mode 100644 apps/api/prisma/migrations/20260813063631_add_wishlist/migration.sql create mode 100644 apps/api/src/modules/customers/customers.controller.ts create mode 100644 apps/api/src/modules/customers/customers.repository.ts create mode 100644 apps/api/src/modules/customers/customers.service.ts create mode 100644 apps/api/src/modules/orders/my-orders.controller.ts create mode 100644 apps/storefront/src/app/[locale]/(account)/layout.tsx create mode 100644 apps/storefront/src/app/[locale]/(account)/login/page.tsx create mode 100644 apps/storefront/src/app/[locale]/(account)/register/page.tsx create mode 100644 apps/storefront/src/features/account/account-panels.tsx create mode 100644 apps/storefront/src/features/account/account-shell.tsx create mode 100644 apps/storefront/src/features/account/auth-form.tsx create mode 100644 apps/storefront/src/features/account/customer-provider.tsx create mode 100644 apps/storefront/src/features/account/wishlist-button.tsx create mode 100644 docs/adr/0023-accounts-adopt-guest-orders-and-never-gate-checkout.md create mode 100644 packages/api-client/src/resources/account.ts create mode 100644 packages/types/src/customers/account.ts create mode 100644 packages/validation/src/customers.ts diff --git a/README.md b/README.md index 7314a41..6848a09 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ sport-store/ │ ├── docs/ │ ├── architecture.md Boundaries, conventions, risks — read this first -│ └── adr/ 22 decision records +│ └── adr/ 23 decision records │ ├── docker-compose.yml Backing services; `--profile full` runs everything ├── turbo.json pnpm-workspace.yaml package.json @@ -254,10 +254,10 @@ locale-in-path would buy nothing. | **M5** ✅ | Cart (Redis), guest checkout, orders with stock reservation and an admin order lifecycle | | **M6** ✅ | Search: PostgreSQL full-text + trigram, diacritic-folded, ranked, with type-ahead and refinable results | | **M7** ◐ | Discounts end-to-end: one engine, one admin screen, stacking, windows, limits, redemptions. Reviews and CMS remain | -| **M8** | Customer account | +| **M8** ✅ | Customer accounts: registration, profile, address book, order history, wishlist; guest orders adopted on sign-up | | **M9** | Payments (VNPay, MoMo, ZaloPay, COD), shipping, notifications | -**Recommended next step: M8 (customer accounts).** The store can +**Recommended next step: M9 (payments) — or M10 (shipping), since payments are deferred.** The store can now be browsed, searched, filled into a bag and checked out, and every order moves stock through a ledger. What it still cannot do is take money — which is the one gap between this and a shop that trades. @@ -270,7 +270,7 @@ Everything below was run, not assumed: - `pnpm lint` · `pnpm typecheck` · `pnpm test` · `pnpm build` — 27/27 Turborepo tasks pass; `pnpm format:check` clean -- 10 migrations, 43 tables; seed loads 36 permissions, 6 roles, 3 brands, 8 categories, +- 11 migrations, 44 tables; seed loads 36 permissions, 6 roles, 3 brands, 8 categories, 3 collections, 12 products, **155 variants**, 64 uploaded images and 3 dev accounts - **78 tests** — RBAC guards, password hashing, translation fallback, `Accept-Language`, the variant matrix planner, the HTTP client's fetch receiver and retry recursion, the inventory @@ -312,6 +312,11 @@ through Chrome with the console and network panel open: at 09:00, not shifted; a discount scheduled for next week reads "Scheduled" and stays out of the bag; switching one off in admin drops it from a shopper's bag on the next load; retiring one soft-deletes it, leaving redemptions and the audit trail intact +- **Accounts (M8):** registering with an email that had guest orders adopts them into the new + account's history; a signed-in checkout attaches the order to the customer while a guest + checkout — and one carrying a malformed token — still succeeds unattached; one customer + cannot read another's order by id, address book or wishlist; an admin token is refused on + account routes; the session survives a page reload with the access token held only in memory - **Content (M7):** a post published in one language still lists on the other locale's journal rather than vanishing; a body containing `