Phase 1: Portal Duplication Cleanup - Delete apps/portal/src/lib/ directory (12 duplicate files) - Update imports to use canonical locations (core/, shared/) Phase 2: Domain Package Standardization - Add contract.ts to notifications and checkout modules - Update billing schema to derive enums from contract Phase 3: BFF Error Handling - Remove hardcoded test SIM number from SimValidationService - Use ConfigService for TEST_SIM_ACCOUNT env variable Phase 4: Circular Dependency Resolution - Create VoiceOptionsModule to break FreebitModule <-> SimManagementModule cycle - Remove forwardRef usage between these modules - Move SimVoiceOptionsService to new voice-options module Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
/**
|
|
* Notifications Schema
|
|
*
|
|
* Zod schemas and types for in-app notifications.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
import { NOTIFICATION_TYPE, NOTIFICATION_SOURCE } from "./contract.js";
|
|
|
|
// =============================================================================
|
|
// Schemas
|
|
// =============================================================================
|
|
|
|
export const notificationSchema = z.object({
|
|
id: z.string().uuid(),
|
|
userId: z.string().uuid(),
|
|
type: z.nativeEnum(NOTIFICATION_TYPE),
|
|
title: z.string(),
|
|
message: z.string().nullable(),
|
|
actionUrl: z.string().nullable(),
|
|
actionLabel: z.string().nullable(),
|
|
source: z.nativeEnum(NOTIFICATION_SOURCE),
|
|
sourceId: z.string().nullable(),
|
|
read: z.boolean(),
|
|
readAt: z.string().datetime().nullable(),
|
|
dismissed: z.boolean(),
|
|
createdAt: z.string().datetime(),
|
|
expiresAt: z.string().datetime(),
|
|
});
|
|
|
|
export type Notification = z.infer<typeof notificationSchema>;
|
|
|
|
export const createNotificationRequestSchema = z.object({
|
|
userId: z.string().uuid(),
|
|
type: z.nativeEnum(NOTIFICATION_TYPE),
|
|
title: z.string().optional(),
|
|
message: z.string().optional(),
|
|
actionUrl: z.string().optional(),
|
|
actionLabel: z.string().optional(),
|
|
source: z.nativeEnum(NOTIFICATION_SOURCE).optional(),
|
|
sourceId: z.string().optional(),
|
|
});
|
|
|
|
export type CreateNotificationRequest = z.infer<typeof createNotificationRequestSchema>;
|
|
|
|
export const notificationListResponseSchema = z.object({
|
|
notifications: z.array(notificationSchema),
|
|
unreadCount: z.number(),
|
|
total: z.number(),
|
|
});
|
|
|
|
export type NotificationListResponse = z.infer<typeof notificationListResponseSchema>;
|
|
|
|
export const notificationUnreadCountResponseSchema = z.object({
|
|
count: z.number(),
|
|
});
|
|
|
|
export type NotificationUnreadCountResponse = z.infer<typeof notificationUnreadCountResponseSchema>;
|
|
|
|
/**
|
|
* Schema for notification query parameters
|
|
*/
|
|
export const notificationQuerySchema = z.object({
|
|
limit: z.coerce.number().int().min(1).max(100).optional().default(20),
|
|
offset: z.coerce.number().int().nonnegative().optional().default(0),
|
|
includeRead: z.coerce.boolean().optional().default(true),
|
|
});
|
|
|
|
export type NotificationQuery = z.infer<typeof notificationQuerySchema>;
|
|
|
|
// =============================================================================
|
|
// Route Param Schemas (BFF)
|
|
// =============================================================================
|
|
|
|
export const notificationIdParamSchema = z.object({
|
|
id: z.string().uuid(),
|
|
});
|
|
export type NotificationIdParam = z.infer<typeof notificationIdParamSchema>;
|