Files
slp-modular-cms/frontend/src/features/offerings/mocks/handlers.ts
T
SluijsensandClaude Sonnet 5 cfb06b28b6
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
Adds the Offerings module and retargets the CI/CD pipeline to Api.SlpSoftware
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
2026-08-02 16:23:09 +02:00

107 lines
3.9 KiB
TypeScript

import { http, HttpResponse } from 'msw';
import type { OfferingAdminDto, CreateOfferingRequest, UpdateOfferingRequest } from '@/features/offerings/services/types';
import { API_BASE } from '@/features/auth/mocks/fixtures';
const seed: OfferingAdminDto[] = [
{
id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
title: 'Starter',
description: 'A simple website',
price: '€ 300',
priceNote: 'one-time',
features: ['1 page', 'Contact form'],
ctaLabel: 'Get started',
featured: false,
displayOrder: 0,
},
{
id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
title: 'Pro',
description: 'A full website',
price: '€ 800',
priceNote: 'one-time',
features: ['5 pages', 'Contact form', 'SEO'],
ctaLabel: 'Get started',
featured: true,
displayOrder: 1,
},
];
let mockOfferings: OfferingAdminDto[] = [...seed];
export const resetMockOfferings = () => {
mockOfferings = [...seed];
};
export const getMockOfferings = () => mockOfferings;
export const offeringsHandlers = [
http.get(`${API_BASE}/api/v1/offerings`, () =>
HttpResponse.json(mockOfferings.map(({ displayOrder: _displayOrder, ...dto }) => dto)),
),
http.get(`${API_BASE}/api/v1/offerings/admin`, () => HttpResponse.json(mockOfferings)),
http.post(`${API_BASE}/api/v1/offerings/admin`, async ({ request }) => {
const body = (await request.json()) as CreateOfferingRequest;
const newOffering: OfferingAdminDto = {
id: crypto.randomUUID(),
...body,
displayOrder: mockOfferings.length,
};
mockOfferings = [...mockOfferings, newOffering];
return HttpResponse.json(newOffering, { status: 201 });
}),
http.put(`${API_BASE}/api/v1/offerings/admin/:id`, async ({ params, request }) => {
const { id } = params as { id: string };
const body = (await request.json()) as UpdateOfferingRequest;
const existing = mockOfferings.find((o) => o.id === id);
if (!existing) return new HttpResponse(null, { status: 404 });
const updated: OfferingAdminDto = { ...existing, ...body };
mockOfferings = mockOfferings.map((o) => (o.id === id ? updated : o));
return HttpResponse.json(updated);
}),
http.delete(`${API_BASE}/api/v1/offerings/admin/:id`, ({ params }) => {
const { id } = params as { id: string };
mockOfferings = mockOfferings.filter((o) => o.id !== id);
return new HttpResponse(null, { status: 204 });
}),
http.put(`${API_BASE}/api/v1/offerings/admin/reorder`, async ({ request }) => {
const body = (await request.json()) as { orderedIds: string[] };
mockOfferings = body.orderedIds
.map((id, index) => {
const offering = mockOfferings.find((o) => o.id === id);
return offering ? { ...offering, displayOrder: index } : null;
})
.filter((o): o is OfferingAdminDto => o !== null);
return new HttpResponse(null, { status: 204 });
}),
http.post(`${API_BASE}/api/v1/offerings/admin/:id/move-up`, ({ params }) => {
const { id } = params as { id: string };
moveAdjacent(id, -1);
return new HttpResponse(null, { status: 204 });
}),
http.post(`${API_BASE}/api/v1/offerings/admin/:id/move-down`, ({ params }) => {
const { id } = params as { id: string };
moveAdjacent(id, 1);
return new HttpResponse(null, { status: 204 });
}),
];
function moveAdjacent(id: string, delta: number) {
const sorted = [...mockOfferings].sort((a, b) => a.displayOrder - b.displayOrder);
const index = sorted.findIndex((o) => o.id === id);
const swapIndex = index + delta;
if (index === -1 || swapIndex < 0 || swapIndex >= sorted.length) return;
const a = sorted[index];
const b = sorted[swapIndex];
[a.displayOrder, b.displayOrder] = [b.displayOrder, a.displayOrder];
}