feat(admin,shop-admin): content/brand management + merchant shop profile (add-content-admin-ui)

This commit is contained in:
Chengdong Zhang
2026-09-23 14:27:00 +08:00
parent 9a749e2551
commit 93e5a05d48
22 changed files with 930 additions and 22 deletions
@@ -0,0 +1,31 @@
## ADDED Requirements
### Requirement: Storefront content management
The platform console SHALL manage the four home-content kinds — banners, promos, quick links and floor adverts — on a content page reachable from the console navigation, loading every entry including inactive ones through the shared API adapter. Each kind SHALL be edited as its ordered list of rows with add, remove and reorder controls (no drag and drop), the per-entry fields the API accepts, an active flag per entry, and a preview of how the storefront renders each entry. Saving a kind SHALL submit the whole edited list in its displayed order as one replacement with the semantics of `PUT /api/admin/content/{kind}`, so positions follow the submitted order and the other three kinds are untouched. Rows SHALL be validated before any request with the rules the API enforces — non-empty image and destination URLs, non-empty glyph, and non-empty `en` and `zh` quick-link labels in `{ en, zh }` JSONB text — with inline field errors on failing rows. Saves SHALL give clear success feedback; a rejected save SHALL keep the edited rows and show a visible failure message.
#### Scenario: save replaces one kind only
- **WHEN** an admin reorders banners, saves that kind, and the mall reloads its home content
- **THEN** banners appear in the new order while promos, quick links and floor adverts are unchanged
#### Scenario: invalid row is blocked before submit
- **WHEN** an admin leaves a banner image URL empty and saves
- **THEN** the row shows an inline field error and no replacement request is sent
#### Scenario: rejected save keeps the editor state
- **WHEN** the API rejects a replacement
- **THEN** the page shows a failure message and keeps the edited rows for correction
### Requirement: Brand management
Platform admins SHALL manage the ordered brand registry on a brands page reachable from the console navigation, through the shared API adapter. The page SHALL edit the whole list — slug, non-empty `{ en, zh }` names and an active flag per row — with add, remove and reorder controls and a preview of each brand as the public list serves it. Saving SHALL replace the whole list in its displayed order so positions follow the submitted order. Slugs SHALL be validated against the ascii `a-z`, `0-9`, `-` pattern and duplicate slugs and incomplete bilingual names SHALL be rejected with inline errors before any request is sent. Saves SHALL give clear success feedback; a rejected save SHALL keep the edited rows and show a visible failure message.
#### Scenario: replace round-trips to the public list
- **WHEN** an admin reorders brands, renames one, and saves
- **THEN** the public brand list returns the same entries in the submitted order
#### Scenario: duplicate slug is blocked before submit
- **WHEN** an admin saves with two rows carrying the same slug
- **THEN** the duplicate rows show inline errors and no replacement request is sent
#### Scenario: save feedback distinguishes success and failure
- **WHEN** a save succeeds or the API refuses the list
- **THEN** the page shows a success confirmation or keeps the edited rows with a failure message, respectively
@@ -0,0 +1,20 @@
## ADDED Requirements
### Requirement: Merchant shop profile editing
Shop users SHALL edit their own shop's profile from a shop-admin page reachable from the shop-scoped navigation, through the shared API adapter's shop-scoped profile update. The form SHALL prefill from the shop's current profile and cover the merchant-writable fields only: logo and banner URLs, company, region, and `{ en, zh }` bilingual address, notice and after-sale copy. Bilingual fields SHALL be validated with non-empty `en` and `zh` text before any request is sent, every write SHALL target the caller's own shop, and profile scores SHALL NOT be editable — they remain platform-set and saving never changes them. Saves SHALL give clear success feedback; a refused save SHALL keep the form values and show a visible failure message.
#### Scenario: merchant updates their own profile
- **WHEN** a shop owner edits their notice, saves, and the mall store page reloads
- **THEN** only that shop shows the new notice and no other shop's profile changes
#### Scenario: incomplete bilingual text is blocked before submit
- **WHEN** a shop owner leaves the `zh` after-sale text empty and saves
- **THEN** the field shows an inline error and no update request is sent
#### Scenario: scores are not editable
- **WHEN** the profile page loads and is saved
- **THEN** no score fields are offered and the platform-set scores remain unchanged
#### Scenario: refused save keeps the form
- **WHEN** the API refuses an update
- **THEN** the page keeps the entered values and shows a failure message
@@ -0,0 +1,20 @@
## ADDED Requirements
### Requirement: Merchant self-service shop profile
A shop owner SHALL set their own shop's profile with `PUT /api/shop/profile`, which upserts the profile row of the shop the caller owns and returns the composed shop profile. The write SHALL require an authenticated user with a shop under the `own_shop` scope and SHALL accept logo and banner URLs, company, region, and bilingual `address`, `notice` and `after_sale` text with the same `{ en, zh }` validation as the platform-admin profile write. Profile scores remain platform-set: merchant-supplied `score_rating`, `score_agreement`, `score_service` or `score_speed` values SHALL NOT be stored. A user without a shop SHALL be refused, and the platform-admin write and public reads SHALL keep their existing behavior.
#### Scenario: merchant upsert round-trips
- **WHEN** a shop owner sets their profile and the storefront reads the shop by slug
- **THEN** the public read returns those values for that shop
#### Scenario: incomplete bilingual text is refused
- **WHEN** a shop owner submits a notice with only `en` text
- **THEN** the request is refused and the stored profile is unchanged
#### Scenario: merchant cannot set scores
- **WHEN** a shop owner submits score values with their profile
- **THEN** the stored scores are unchanged
#### Scenario: a user without a shop is refused
- **WHEN** a signed-in customer sends the merchant profile write
- **THEN** the API refuses the write