feat(admin): vocabularies page for per-port pick lists
New /admin/vocabularies route + VocabulariesManager component. Catalog at src/lib/vocabularies.ts defines 11 vocabularies grouped into Interests / Berths / Expenses / Documents domains, each shipping with the canonical defaults from src/lib/constants.ts (interest temps, status-change reasons, tenure types, expense categories, document types, plus the 5 berth-spec dropdowns). Editor supports add / remove / reorder / inline-rename / reset-to- defaults; only dirty cards save. Uses the existing /api/v1/admin/settings PUT endpoint (already gated on admin.manage_settings) so storage piggybacks on system_settings (port_id, key) per the established pattern. Reps need read access without holding manage_settings — added a public-read /api/v1/vocabularies endpoint plus useVocabulary() hook (5-minute staleTime). The admin manager invalidates the vocabularies query on save so consumers (status-change dialog, expense form, etc.) pick up new lists immediately. Adds a Vocabularies card to the admin landing page. Follow-up sweep owed: actual consumers (interest-card temperature pill, berth-tabs select dropdowns, expense form category list, etc.) still read from the hardcoded constants.ts arrays. Wire them through useVocabulary in a separate pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
src/app/api/v1/vocabularies/route.ts
Normal file
33
src/app/api/v1/vocabularies/route.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { withAuth } from '@/lib/api/helpers';
|
||||
import { errorResponse } from '@/lib/errors';
|
||||
import { listSettings } from '@/lib/services/settings.service';
|
||||
import { resolveVocabulary, VOCABULARIES, type VocabularyKey } from '@/lib/vocabularies';
|
||||
|
||||
/**
|
||||
* GET /api/v1/vocabularies
|
||||
*
|
||||
* Returns the resolved per-port vocabulary lists (admin overrides
|
||||
* merged with shipped defaults). Any authenticated user in this port
|
||||
* can read — vocabularies drive in-app pickers (status reasons,
|
||||
* interest temperatures, expense categories, etc.) so reps need read
|
||||
* access without holding `admin.manage_settings`.
|
||||
*
|
||||
* Edits still go through `/api/v1/admin/settings` (admin-only).
|
||||
*/
|
||||
export const GET = withAuth(async (_req, ctx) => {
|
||||
try {
|
||||
const { portSettings } = await listSettings(ctx.portId);
|
||||
const overridesByKey = new Map<string, unknown>();
|
||||
for (const row of portSettings) overridesByKey.set(row.key, row.value);
|
||||
|
||||
const data: Record<string, readonly string[]> = {};
|
||||
for (const def of VOCABULARIES) {
|
||||
data[def.key] = resolveVocabulary(def.key as VocabularyKey, overridesByKey.get(def.key));
|
||||
}
|
||||
return NextResponse.json({ data });
|
||||
} catch (error) {
|
||||
return errorResponse(error);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user