File size: 2,142 Bytes
cb43fbd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import { z } from 'zod';

/**
 * System-notice API contract — the /api/system-notices endpoints.
 *
 * Notices are server-side announcements (release notes, onboarding hints, ...)
 * defined in a static registry. The server evaluates each notice's conditions
 * for the current user and returns only the active, non-dismissed ones, sorted
 * by priority/severity/date. The DTO sent to the client is the notice minus the
 * server-only fields (conditions, publishedAt, version bounds, priority) — see
 * SystemNoticeDTO in server/src/systemNotices/types.ts, which this mirrors.
 *
 * The bespoke 404 `{ error: 'NOTICE_NOT_FOUND' }` body and the 204 dismiss
 * response are reproduced in the controller, not derived from this schema.
 */

export const noticeDisplaySchema = z.enum(['modal', 'banner', 'toast']);
export const noticeSeveritySchema = z.enum(['info', 'warn', 'critical']);

const noticeMediaSchema = z.object({
  src: z.string(),
  srcDark: z.string().optional(),
  altKey: z.string(),
  placement: z.enum(['hero', 'inline']).optional(),
  aspectRatio: z.string().optional(),
});

const noticeHighlightSchema = z.object({
  labelKey: z.string(),
  iconName: z.string().optional(),
});

/** Call-to-action: either a navigation link or an in-app action. */
const noticeCtaSchema = z.discriminatedUnion('kind', [
  z.object({ kind: z.literal('nav'), labelKey: z.string(), href: z.string() }),
  z.object({
    kind: z.literal('action'),
    labelKey: z.string(),
    actionId: z.string(),
    dismissOnAction: z.boolean().optional(),
  }),
]);

/** The client-facing notice (server-evaluated; conditions/versioning stripped). */
export const systemNoticeDtoSchema = z.object({
  id: z.string(),
  display: noticeDisplaySchema,
  severity: noticeSeveritySchema,
  titleKey: z.string(),
  bodyKey: z.string(),
  bodyParams: z.record(z.string(), z.string()).optional(),
  icon: z.string().optional(),
  media: noticeMediaSchema.optional(),
  highlights: z.array(noticeHighlightSchema).optional(),
  cta: noticeCtaSchema.optional(),
  dismissible: z.boolean(),
});
export type SystemNoticeDto = z.infer<typeof systemNoticeDtoSchema>;