# 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**: `Offering` entity (13 fields per `domain-entities.md`), `OfferingsDbContext` — table `OfferingsOfferings`, `Features` stored as a JSON column (`List` with an EF Core `ValueComparer`), soft-delete enforced via a global `HasQueryFilter`. - **Repository**: `IOfferingRepository`/`OfferingRepository` — CRUD plus `GetMaxDisplayOrderAsync`, `GetFeaturedAsync`, `GetPreviousAsync`/`GetNextAsync` (adjacent-swap lookups), and a `BeginTransactionAsync` wrapper. - **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. `LastModifiedByUserId` set on every create/update/delete (NFR-OFF-03); one `LogInformation` structured log entry per mutation. - **Models**: `OfferingDto` (public), `OfferingAdminDto` (admin, adds `DisplayOrder`), `CreateOfferingRequest`/`UpdateOfferingRequest` (DataAnnotations + a custom `FeaturesValidationAttribute` for the 1-10-items/≤200-chars rule), `ReorderOfferingsRequest`. - **Controller**: `OfferingsController` — route/auth table exactly per `component-methods.md` (`GET /api/v1/offerings` public + `[EnableRateLimiting("offerings-public")]`; admin CRUD/reorder/move under `AdminOnly`). - **Module**: `OfferingsModule` (`IModule`) — registers the DbContext (Pomelo MySQL, `NonLockingMySQLHistoryRepository`), repository, service; migrates on startup. - **Rate limiting**: new `offerings-public` `FixedWindowLimiter` policy added to `SlpModularCms.Core.Hosting.ServiceCollectionExtensions.AddCmsRateLimiting`, config-driven via `RateLimiting:OfferingsPublic` (added to `Api.SlpSoftware/appsettings.json`). - **Migration**: `InitialCreate` (EF Core, generated against `Api.SlpSoftware` as startup project). - **Wiring**: `SlpModularCms.Modules.Offerings`/`.Tests` added to the solution (Application/Modules and Tests/Modules folders per `CLAUDE.md`); `` added to `Api.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/utilities` added (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 to `useReorderOfferings`. - **Components**: `OfferingsList` (`DndContext`/`SortableContext`), `OfferingRow` (drag handle, featured toggle, move up/down, edit/delete actions, all `data-testid`s per `frontend-components.md`), `DeleteOfferingDialog`, `OfferingForm` (react-hook-form + zod, dynamic features list managed via `setValue` rather than `useFieldArray` since `features` is a plain `string[]`). - **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 behind `RoleGuard(Owner, Administrator)` + `ModuleGuard(requiredModule="Offerings")`; `Sidebar.tsx` nav entry; full `nav.offerings`/`offerings.*` key sets added to both `en`/`nl` locale files. - **Tests**: schema tests, an `OfferingsListPage` integration suite (title/list/empty-state/delete-confirm/delete-cancel/move-button-boundaries/auth-redirect), an `OfferingFormPage` suite (create/validation-error/edit-prefill) — all via MSW-mocked handlers in `features/offerings/mocks/handlers.ts`, following the `features/cms` test-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 lint` clean (one `react-hooks/set-state-in-effect` violation in `useOfferingsDnd` found and fixed — switched to the "adjust state during render" pattern); `pnpm test` green, 254/254 across 41 files (43 new). ## Deviations From the Plan Worth Noting - `useFieldArray` was planned implicitly for the dynamic features list but doesn't type-check cleanly against a plain `string[]` field — used `useWatch` + `setValue` instead, a standard react-hook-form alternative for primitive arrays. - `Modules.Offerings.csproj` does not reference `Microsoft.EntityFrameworkCore.Design` — confirmed by inspecting `Modules.Master.csproj` that this package belongs on the *startup* project (`Api.SlpSoftware`, which already has it), not on every project with a `DbContext`.