barsa f447ba1800 Refactor codebase: eliminate duplication, standardize patterns, resolve circular deps
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>
2026-01-13 14:25:14 +09:00

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>;