Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project in the Clients solution folder, intended to eventually become the deployed API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as SlpModularCms.Api plus a future Offerings module. - Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), shared by both Client projects so they cannot drift apart - Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into Core, since CmsHost cannot live in Api but Core cannot depend on Api - Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database and dev ports (5286/7223, distinct from Api's and Api.Slave's) - Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline regression tests (security headers, health check, SPA fallback, rate limiting), scoped to Api per NFR Design - Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave - Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing refresh token) as unhandled errors -- pre-existing, unrelated to this feature's own scope, found while testing the new instance Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
10 KiB
User Stories — SlpSoftware Production API
Breakdown approach: persona-based (approved). Acceptance criteria format: Given/When/Then (approved).
Epic A — Site Visitor: Viewing Offerings
US-01 — View the list of available offerings
As a Site Visitor, I want to see the current list of offerings on the website, so that I can compare what's available and pick one that fits my needs.
Acceptance Criteria
- Given one or more offerings exist in the CMS, when the frontend requests
GET /api/v1/offerings, then the response is200 OKwith a JSON array of offerings in display order, each includingid,title,description,price,priceNote,features,ctaLabel, andfeatured. - Given the offerings are returned, when rendered, then the array order is the same order the CMS Administrator configured (FR-5
DisplayOrder) — no re-sorting happens client-side or server-side beyond that stored order.
Traceability: FR-5, FR-6
US-02 — See the recommended offering highlighted
As a Site Visitor, I want to see which offering is the "most chosen" one, so that I have a quick recommendation if I'm unsure which package to pick.
Acceptance Criteria
- Given the CMS Administrator has marked exactly one offering as featured, when
GET /api/v1/offeringsis called, then exactly one item in the response has"featured": trueand all others have"featured": false. - Given no offering has been explicitly marked as featured, when
GET /api/v1/offeringsis called, then every item has"featured": false(no forced default featured item).
Traceability: FR-5, FR-6; enforcement mechanism defined in US-10
US-03 — Website stays functional with zero offerings
As a Site Visitor, I want the site to still work correctly even if no offerings have been configured yet, so that I don't encounter a broken page during initial setup or content maintenance.
Acceptance Criteria
- Given zero offerings exist in the CMS, when the frontend requests
GET /api/v1/offerings, then the response is200 OKwith an empty JSON array[]— not an error response. - Given this empty-array response, then it is the frontend's responsibility (out of scope for this feature) to decide how to render an empty state; the API's only obligation is a valid, non-error response.
Traceability: FR-6; decision Q4 = A (allow empty list, no deletion guard)
Epic B — CMS Administrator: Managing Offerings
US-04 — Create a new offering
As a CMS Administrator, I want to create a new offering with title, description, price, price note, features, and CTA label, so that I can add a new package/service to the website without a code deploy.
Acceptance Criteria
- Given I am authenticated with at least the
Administratorrole, when I submit a new offering with all required fields (Title,Description,Price,PriceNote, at least oneFeature,CtaLabel), then the offering is persisted and immediately appears inGET /api/v1/offeringsat the end of the display order. - Given I create an offering without marking it featured, when it is saved, then
featureddefaults tofalse.
Traceability: FR-5, FR-7
US-05 — Edit an existing offering
As a CMS Administrator, I want to edit an existing offering's content, so that I can correct or update pricing and copy as the business changes.
Acceptance Criteria
- Given an existing offering, when I update any of its fields and save, then
GET /api/v1/offeringsreflects the new values on the next request. - Given I edit an offering, when I save it, then its
id(stable slug) andDisplayOrderare not changed as a side effect of the edit — only reordering (US-08/US-09) changes order.
Traceability: FR-5, FR-7
US-06 — Delete an offering
As a CMS Administrator, I want to delete an offering that's no longer relevant, so that the website doesn't show outdated packages.
Acceptance Criteria
- Given more than one offering exists, when I delete one of them, then it no longer appears in
GET /api/v1/offerings, and the remaining offerings keep their relative display order.
Traceability: FR-7
US-07 — Delete the last remaining offering
As a CMS Administrator, I want to be able to delete the last remaining offering if needed, so that I'm not blocked from clearing content during a redesign or content pause, even though it temporarily leaves the site with nothing to show.
Acceptance Criteria
- Given exactly one offering exists, when I delete it, then the deletion succeeds (no blocking validation error) and
GET /api/v1/offeringssubsequently returns[].
Traceability: FR-7; decision Q4 = A (deletion is never blocked to prevent an empty list)
US-08 — Reorder offerings via drag-and-drop
As a CMS Administrator, I want to reorder offerings by dragging them into a new position in the list, so that I can control the order visitors see them in without editing a numeric field.
Acceptance Criteria
- Given two or more offerings, when I drag one to a new position and the change is saved, then the
DisplayOrdervalues are updated soGET /api/v1/offeringsreflects the new order.
Traceability: FR-5, FR-7; decision Q5 = A+B
US-09 — Reorder offerings via up/down buttons
As a CMS Administrator, I want an alternative to drag-and-drop — explicit "move up" / "move down" controls per row, so that I can reorder offerings accurately even without a mouse, or when drag-and-drop is impractical (accessibility, precision).
Acceptance Criteria
- Given an offering that is not first in the list, when I use its "move up" control, then it swaps display order with the offering immediately before it.
- Given an offering that is not last in the list, when I use its "move down" control, then it swaps display order with the offering immediately after it.
- Given the first offering in the list, then its "move up" control is disabled (and symmetrically for "move down" on the last offering).
Traceability: FR-5, FR-7; decision Q5 = A+B (accessible fallback alongside drag-and-drop)
US-10 — Mark an offering as featured (system-enforced exclusivity)
As a CMS Administrator, I want the system to guarantee that at most one offering is marked "featured" at any time, so that I don't accidentally end up with a confusing website showing more than one "most chosen" badge (per the external hand-off doc's expectation, which the frontend itself does not enforce).
Acceptance Criteria
- Given offering A is currently featured, when I mark offering B as featured, then offering A is automatically un-featured in the same operation — the system never persists more than one featured offering at a time.
- Given no offering is currently featured, when I mark one as featured, then exactly that one becomes featured.
- Given the currently featured offering, when I explicitly un-feature it (without featuring another), then zero offerings are featured — this is a valid state (see US-02).
Traceability: FR-5, FR-7; decision Q3 = A
US-11 — Receive validation feedback on invalid input
As a CMS Administrator, I want clear validation errors when I submit incomplete or malformed offering data, so that I can fix my mistake instead of silently corrupting the website's content.
Acceptance Criteria
- Given I submit an offering missing a required field (
Title,Description,Price,PriceNote,CtaLabel, orFeatures), when I save, then the request is rejected with a validation error identifying which field(s) are invalid, and no partial record is persisted. - Given I submit a field exceeding its defined maximum length, when I save, then the request is rejected the same way.
Traceability: FR-7; SECURITY-05 (Input Validation)
US-12 — Admin actions require the Administrator role
As a CMS Administrator, I want offering management to be inaccessible to anyone without at least the Administrator role, so that unauthenticated visitors or lower-privileged users can't alter the website's content.
Acceptance Criteria
- Given an unauthenticated request, when it targets any admin offerings endpoint (create/edit/delete/reorder), then it is rejected with
401 Unauthorized. - Given an authenticated request from a
User-role account (belowAdministratorin the hierarchy), when it targets any admin offerings endpoint, then it is rejected with403 Forbidden. - Given an authenticated request from an
Administrator- orOwner-role account, when it targets any admin offerings endpoint, then it is permitted (subject to the other acceptance criteria above).
Traceability: FR-7; SECURITY-06, SECURITY-08
Persona-to-Story Mapping
| Persona | Stories |
|---|---|
| Site Visitor | US-01, US-02, US-03 |
| CMS Administrator | US-04, US-05, US-06, US-07, US-08, US-09, US-10, US-11, US-12 |
INVEST Self-Check
| Story | Independent | Negotiable | Valuable | Estimable | Small | Testable |
|---|---|---|---|---|---|---|
| US-01..US-03 | ✅ each stands alone | ✅ display details open | ✅ core public value | ✅ | ✅ | ✅ Given/When/Then |
| US-04..US-07 | ✅ each CRUD op independent | ✅ UI details open | ✅ core admin value | ✅ | ✅ | ✅ Given/When/Then |
| US-08, US-09 | ✅ each interaction mode independent | ✅ exact UI open | ✅ usability | ✅ | ✅ | ✅ Given/When/Then |
| US-10 | ✅ | ✅ | ✅ prevents a real content bug | ✅ | ✅ | ✅ Given/When/Then |
| US-11, US-12 | ✅ | ✅ | ✅ security/data-quality value | ✅ | ✅ | ✅ Given/When/Then |
2 personas, 12 user stories (3 Site Visitor, 9 CMS Administrator), all with Given/When/Then acceptance criteria and explicit traceability to functional requirements and/or Security Baseline rules.