Continuous Integration / config (pull_request) Successful in 12s
Continuous Integration / changes (pull_request) Successful in 22s
Continuous Integration / backend-build (pull_request) Successful in 5m53s
Continuous Integration / vulnerability-scan (pull_request) Successful in 5m46s
Continuous Integration / frontend-prepare (pull_request) Successful in 1m54s
Continuous Integration / backend-test (pull_request) Successful in 7m37s
Continuous Integration / frontend-build (pull_request) Successful in 2m14s
Continuous Integration / frontend-test (pull_request) Successful in 4m59s
Continuous Integration / frontend-lint (pull_request) Successful in 2m2s
Continuous Integration / publish-production (pull_request) Skipped
Continuous Integration / deploy-production (pull_request) Skipped
Continuous Integration / publish-test (pull_request) Successful in 7m34s
Continuous Integration / deploy-test (pull_request) Skipped
Implements Unit 2 "Offerings" (backend module, admin CRUD UI with drag-and-drop reordering, public GET /api/v1/offerings endpoint) and executes the feature's D-15 CI/CD cutover, switching the deploy pipeline's build/publish target from SlpModularCms.Api to SlpModularCms.Api.SlpSoftware. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
5.2 KiB
5.2 KiB
Code Generation Summary — Unit: Offerings
Implements all 12 user stories (US-01–US-12) and FR-4/5/6/7/8, per offerings-code-generation-plan.md.
Backend — SlpModularCms.Modules.Offerings
- Domain:
Offeringentity (13 fields perdomain-entities.md),OfferingsDbContext— tableOfferingsOfferings,Featuresstored as a JSON column (List<string>with an EF CoreValueComparer), soft-delete enforced via a globalHasQueryFilter. - Repository:
IOfferingRepository/OfferingRepository— CRUD plusGetMaxDisplayOrderAsync,GetFeaturedAsync,GetPreviousAsync/GetNextAsync(adjacent-swap lookups), and aBeginTransactionAsyncwrapper. - Service:
IOfferingsService/OfferingsService— BR-OFF-01 (featured exclusivity), BR-OFF-02 (soft delete, never blocked), BR-OFF-03 (reorder + adjacent-swap boundaries), BR-OFF-04 (validation, enforced at the model-binding layer). Multi-row operations (featured swap, reorder, adjacent swap) run inside an explicit EF Core transaction per NFR Design Pattern 1.LastModifiedByUserIdset on every create/update/delete (NFR-OFF-03); oneLogInformationstructured log entry per mutation. - Models:
OfferingDto(public),OfferingAdminDto(admin, addsDisplayOrder),CreateOfferingRequest/UpdateOfferingRequest(DataAnnotations + a customFeaturesValidationAttributefor the 1-10-items/≤200-chars rule),ReorderOfferingsRequest. - Controller:
OfferingsController— route/auth table exactly percomponent-methods.md(GET /api/v1/offeringspublic +[EnableRateLimiting("offerings-public")]; admin CRUD/reorder/move underAdminOnly). - Module:
OfferingsModule(IModule) — registers the DbContext (Pomelo MySQL,NonLockingMySQLHistoryRepository), repository, service; migrates on startup. - Rate limiting: new
offerings-publicFixedWindowLimiterpolicy added toSlpModularCms.Core.Hosting.ServiceCollectionExtensions.AddCmsRateLimiting, config-driven viaRateLimiting:OfferingsPublic(added toApi.SlpSoftware/appsettings.json). - Migration:
InitialCreate(EF Core, generated againstApi.SlpSoftwareas startup project). - Wiring:
SlpModularCms.Modules.Offerings/.Testsadded to the solution (Application/Modules and Tests/Modules folders perCLAUDE.md);<ProjectReference>added toApi.SlpSoftware.csproj. - Tests: 38 tests —
OfferingRepositoryTests(EF Core InMemory),OfferingsServiceTests(NSubstitute),OfferingsControllerTests. All four business rules covered, plus the soft-delete query filter.
Frontend — frontend/src/features/offerings/
- Dependency:
@dnd-kit/core,@dnd-kit/sortable,@dnd-kit/utilitiesadded (Functional Design Q1 = A). - Schema:
schemas/offering.ts(zod, mirrors BR-OFF-04 exactly). - Services (API-calling hooks):
useOfferings,useOffering(derived from the admin-list cache),useCreateOffering,useUpdateOffering,useDeleteOffering,useReorderOfferings,useMoveOffering. - Hooks (feature-local):
useOfferingsDnd— dnd-kit sensor setup, drag-end reordering, optimistic local state, delegates persistence touseReorderOfferings. - Components:
OfferingsList(DndContext/SortableContext),OfferingRow(drag handle, featured toggle, move up/down, edit/delete actions, alldata-testids perfrontend-components.md),DeleteOfferingDialog,OfferingForm(react-hook-form + zod, dynamic features list managed viasetValuerather thanuseFieldArraysincefeaturesis a plainstring[]). - Pages:
OfferingsListPage,OfferingFormPage(shared by create/edit, per Functional Design Q2). - Routing/nav/i18n: three routes under
authenticatedRoute(/offerings,/offerings/new,/offerings/$id/edit), each behindRoleGuard(Owner, Administrator)+ModuleGuard(requiredModule="Offerings");Sidebar.tsxnav entry; fullnav.offerings/offerings.*key sets added to bothen/nllocale files. - Tests: schema tests, an
OfferingsListPageintegration suite (title/list/empty-state/delete-confirm/delete-cancel/move-button-boundaries/auth-redirect), anOfferingFormPagesuite (create/validation-error/edit-prefill) — all via MSW-mocked handlers infeatures/offerings/mocks/handlers.ts, following thefeatures/cmstest-style precedent.
Verification
- Backend: full solution build succeeded; full test suite green, 414/414 (38 new in
Modules.Offerings.Tests, no regressions elsewhere). - Frontend:
pnpm build(tsc + vite) succeeded;pnpm lintclean (onereact-hooks/set-state-in-effectviolation inuseOfferingsDndfound and fixed — switched to the "adjust state during render" pattern);pnpm testgreen, 254/254 across 41 files (43 new).
Deviations From the Plan Worth Noting
useFieldArraywas planned implicitly for the dynamic features list but doesn't type-check cleanly against a plainstring[]field — useduseWatch+setValueinstead, a standard react-hook-form alternative for primitive arrays.Modules.Offerings.csprojdoes not referenceMicrosoft.EntityFrameworkCore.Design— confirmed by inspectingModules.Master.csprojthat this package belongs on the startup project (Api.SlpSoftware, which already has it), not on every project with aDbContext.