mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-22 06:41:46 +00:00
feat(notices): add system notice infrastructure
Server-side notice registry with per-user condition evaluation (firstLogin, existingUserBeforeVersion, addonEnabled, dateWindow, role, custom). Notices are sorted by priority then severity, filtered against dismissals stored in a new user_notice_dismissals table, and served via GET /api/system-notices/active + POST /api/system-notices/:id/dismiss. Client renders notices through a host component that partitions by display type (modal / banner / toast). The modal renderer supports multi-page pagination with directional slide transitions, keyboard navigation, and correct dismiss-all semantics on CTA / X / ESC. Dismissals are optimistic with a single background retry. Includes 3.0.0 upgrade notices (v3-photos, v3-journey, v3-features), onboarding welcome modal, and full i18n coverage across 15 languages. The /journey route is addon-gated on both client and server. Also includes: unit + integration test suites, registry integrity test that validates action CTA IDs against client source, and technical documentation in docs/system-notices.md.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import type { SystemNotice } from './types.js';
|
||||
|
||||
/**
|
||||
* SYSTEM NOTICE REGISTRY
|
||||
*
|
||||
* Rules for authoring:
|
||||
* - NEVER remove or renumber entries — dismissal tracking is keyed by `id`.
|
||||
* - `id` must be globally unique and stable across deployments.
|
||||
* - Title: ≤40 chars, sentence case, no trailing punctuation.
|
||||
* - Body: markdown (modal) or plain text (banner/toast). ≤400/140/80 chars.
|
||||
* - CTA label: ≤20 chars, a verb.
|
||||
* - Never hardcode version numbers/dates in translated strings — use bodyParams.
|
||||
* - See plans/system-notices/00-overview.md for full authoring guidelines.
|
||||
*/
|
||||
export const SYSTEM_NOTICES: SystemNotice[] = [
|
||||
// ── 3.0.0 upgrade notices (shown as a multipage modal to pre-3.0 users) ─────
|
||||
|
||||
{
|
||||
// Page 1 — breaking change first (warn → sorts before the two info notices)
|
||||
id: 'v3-photos',
|
||||
display: 'modal',
|
||||
severity: 'warn',
|
||||
icon: 'ImageOff',
|
||||
titleKey: 'system_notice.v3_photos.title',
|
||||
bodyKey: 'system_notice.v3_photos.body',
|
||||
dismissible: true,
|
||||
conditions: [{ kind: 'existingUserBeforeVersion', version: '3.0.0' }],
|
||||
publishedAt: '2026-04-16T00:00:00Z',
|
||||
priority: 90,
|
||||
},
|
||||
|
||||
{
|
||||
// Page 2 — flagship feature (only when Journey addon is enabled)
|
||||
id: 'v3-journey',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
icon: 'BookOpen',
|
||||
titleKey: 'system_notice.v3_journey.title',
|
||||
bodyKey: 'system_notice.v3_journey.body',
|
||||
highlights: [
|
||||
{ labelKey: 'system_notice.v3_journey.highlight_timeline', iconName: 'CalendarDays' },
|
||||
{ labelKey: 'system_notice.v3_journey.highlight_photos', iconName: 'Images' },
|
||||
{ labelKey: 'system_notice.v3_journey.highlight_share', iconName: 'Globe' },
|
||||
{ labelKey: 'system_notice.v3_journey.highlight_export', iconName: 'FileText' },
|
||||
],
|
||||
cta: {
|
||||
kind: 'nav',
|
||||
labelKey: 'system_notice.v3_journey.cta_label',
|
||||
href: '/journey',
|
||||
},
|
||||
dismissible: true,
|
||||
conditions: [
|
||||
{ kind: 'existingUserBeforeVersion', version: '3.0.0' },
|
||||
{ kind: 'addonEnabled', addonId: 'journey' },
|
||||
],
|
||||
publishedAt: '2026-04-16T00:00:00Z',
|
||||
priority: 80,
|
||||
},
|
||||
|
||||
{
|
||||
// Page 3 — other highlights
|
||||
id: 'v3-features',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
icon: 'Sparkles',
|
||||
titleKey: 'system_notice.v3_features.title',
|
||||
bodyKey: 'system_notice.v3_features.body',
|
||||
highlights: [
|
||||
{ labelKey: 'system_notice.v3_features.highlight_dashboard', iconName: 'LayoutDashboard' },
|
||||
{ labelKey: 'system_notice.v3_features.highlight_offline', iconName: 'WifiOff' },
|
||||
{ labelKey: 'system_notice.v3_features.highlight_search', iconName: 'Search' },
|
||||
{ labelKey: 'system_notice.v3_features.highlight_import', iconName: 'FileInput' },
|
||||
],
|
||||
dismissible: true,
|
||||
conditions: [{ kind: 'existingUserBeforeVersion', version: '3.0.0' }],
|
||||
publishedAt: '2026-04-16T00:00:00Z',
|
||||
priority: 70,
|
||||
},
|
||||
|
||||
// ── Onboarding ─────────────────────────────────────────────────────────────
|
||||
|
||||
{
|
||||
id: 'welcome-v1',
|
||||
display: 'modal',
|
||||
severity: 'info',
|
||||
icon: 'Sparkles',
|
||||
titleKey: 'system_notice.welcome_v1.title',
|
||||
bodyKey: 'system_notice.welcome_v1.body',
|
||||
highlights: [
|
||||
{ labelKey: 'system_notice.welcome_v1.highlight_plan', iconName: 'Map' },
|
||||
{ labelKey: 'system_notice.welcome_v1.highlight_share', iconName: 'Users' },
|
||||
{ labelKey: 'system_notice.welcome_v1.highlight_offline', iconName: 'WifiOff' },
|
||||
],
|
||||
cta: {
|
||||
kind: 'action',
|
||||
labelKey: 'system_notice.welcome_v1.cta_label',
|
||||
actionId: 'open:trip-create',
|
||||
},
|
||||
dismissible: true,
|
||||
conditions: [{ kind: 'firstLogin' }],
|
||||
publishedAt: '2026-04-16T00:00:00Z',
|
||||
priority: 100,
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user