Replace loose z.string() fields in supportCaseSchema and supportCaseFilterSchema with the already-defined enum schemas (status, priority, category). Add JSDoc to intentional escape hatches in customer contract interfaces. Fix portal type assertions for the stricter filter types.
190 lines
6.3 KiB
TypeScript
190 lines
6.3 KiB
TypeScript
import { z } from "zod";
|
|
import { SUPPORT_CASE_STATUS, SUPPORT_CASE_PRIORITY, SUPPORT_CASE_CATEGORY } from "./contract.js";
|
|
|
|
/**
|
|
* Portal status values - customer-friendly statuses only
|
|
*
|
|
* Internal Salesforce statuses are mapped to these customer-facing values:
|
|
* - "新規" → New
|
|
* - "対応中", "Awaiting Approval", "VPN Pending", "Pending" → In Progress
|
|
* - "完了済み" (Replied) → Awaiting Customer
|
|
* - "Closed" → Closed
|
|
*/
|
|
const supportCaseStatusValues = [
|
|
SUPPORT_CASE_STATUS.NEW,
|
|
SUPPORT_CASE_STATUS.IN_PROGRESS,
|
|
SUPPORT_CASE_STATUS.AWAITING_CUSTOMER,
|
|
SUPPORT_CASE_STATUS.CLOSED,
|
|
] as const;
|
|
|
|
/**
|
|
* Portal priority values (mapped from Salesforce Japanese API names)
|
|
*/
|
|
const supportCasePriorityValues = [
|
|
SUPPORT_CASE_PRIORITY.LOW,
|
|
SUPPORT_CASE_PRIORITY.MEDIUM,
|
|
SUPPORT_CASE_PRIORITY.HIGH,
|
|
] as const;
|
|
|
|
const supportCaseCategoryValues = [
|
|
SUPPORT_CASE_CATEGORY.TECHNICAL,
|
|
SUPPORT_CASE_CATEGORY.BILLING,
|
|
SUPPORT_CASE_CATEGORY.GENERAL,
|
|
SUPPORT_CASE_CATEGORY.FEATURE_REQUEST,
|
|
] as const;
|
|
|
|
export const supportCaseStatusSchema = z.enum(supportCaseStatusValues);
|
|
export const supportCasePrioritySchema = z.enum(supportCasePriorityValues);
|
|
export const supportCaseCategorySchema = z.enum(supportCaseCategoryValues);
|
|
|
|
/**
|
|
* Support case schema - compatible with Salesforce Case object
|
|
* ID is a string (Salesforce ID format: 15 or 18 char alphanumeric)
|
|
*/
|
|
export const supportCaseSchema = z.object({
|
|
id: z.string().min(15).max(18),
|
|
caseNumber: z.string(),
|
|
subject: z.string().min(1),
|
|
status: supportCaseStatusSchema,
|
|
priority: supportCasePrioritySchema,
|
|
category: supportCaseCategorySchema.nullable(),
|
|
createdAt: z.string(),
|
|
updatedAt: z.string(),
|
|
closedAt: z.string().nullable(),
|
|
description: z.string(),
|
|
});
|
|
|
|
export const supportCaseSummarySchema = z.object({
|
|
total: z.number().int().nonnegative(),
|
|
open: z.number().int().nonnegative(),
|
|
highPriority: z.number().int().nonnegative(),
|
|
resolved: z.number().int().nonnegative(),
|
|
});
|
|
|
|
export const supportCaseListSchema = z.object({
|
|
cases: z.array(supportCaseSchema),
|
|
summary: supportCaseSummarySchema,
|
|
});
|
|
|
|
export const supportCaseFilterSchema = z
|
|
.object({
|
|
status: supportCaseStatusSchema.optional(),
|
|
priority: supportCasePrioritySchema.optional(),
|
|
category: supportCaseCategorySchema.optional(),
|
|
search: z.string().trim().min(1).optional(),
|
|
})
|
|
.default({});
|
|
|
|
/**
|
|
* Request schema for creating a new support case
|
|
*/
|
|
export const createCaseRequestSchema = z.object({
|
|
subject: z.string().min(1).max(255),
|
|
description: z.string().min(1).max(32000),
|
|
category: supportCaseCategorySchema.optional(),
|
|
priority: supportCasePrioritySchema.optional(),
|
|
});
|
|
|
|
/**
|
|
* Response schema for case creation
|
|
*/
|
|
export const createCaseResponseSchema = z.object({
|
|
id: z.string(),
|
|
caseNumber: z.string(),
|
|
});
|
|
|
|
/**
|
|
* Public contact form schema (unauthenticated)
|
|
*/
|
|
export const publicContactRequestSchema = z.object({
|
|
name: z.string().min(1, "Name is required"),
|
|
email: z.string().email("Valid email required"),
|
|
phone: z.string().optional(),
|
|
subject: z.string().min(1, "Subject is required"),
|
|
message: z.string().min(10, "Message must be at least 10 characters"),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Case Message Schemas (for conversation view)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Message type - either from email exchange or case comment
|
|
*/
|
|
export const caseMessageTypeSchema = z.enum(["email", "comment"]);
|
|
|
|
/**
|
|
* Message direction for emails
|
|
*/
|
|
export const caseMessageDirectionSchema = z.enum(["inbound", "outbound"]);
|
|
|
|
/**
|
|
* Unified case message schema - represents either an EmailMessage or CaseComment
|
|
* Used for displaying conversation threads in the portal
|
|
*/
|
|
export const caseMessageSchema = z.object({
|
|
/** Unique identifier (EmailMessage.Id or CaseComment.Id) */
|
|
id: z.string(),
|
|
/** Message type: email or comment */
|
|
type: caseMessageTypeSchema,
|
|
/** Message body/content */
|
|
body: z.string(),
|
|
/** Who sent/wrote the message */
|
|
author: z.object({
|
|
name: z.string(),
|
|
email: z.string().nullable(),
|
|
isCustomer: z.boolean(),
|
|
}),
|
|
/** When the message was created/sent */
|
|
createdAt: z.string(),
|
|
/** For emails: inbound (customer→agent) or outbound (agent→customer) */
|
|
direction: caseMessageDirectionSchema.nullable(),
|
|
/** Whether the message has attachments (for emails) */
|
|
hasAttachment: z.boolean().optional(),
|
|
});
|
|
|
|
/**
|
|
* List of case messages for conversation view
|
|
*/
|
|
export const caseMessageListSchema = z.object({
|
|
messages: z.array(caseMessageSchema),
|
|
/** Case thread identifier for email threading */
|
|
threadId: z.string().nullable(),
|
|
});
|
|
|
|
/**
|
|
* Request schema for adding a comment to a case
|
|
*/
|
|
export const addCaseCommentRequestSchema = z.object({
|
|
body: z.string().min(1, "Message is required").max(32000),
|
|
});
|
|
|
|
/**
|
|
* Response schema for adding a comment
|
|
*/
|
|
export const addCaseCommentResponseSchema = z.object({
|
|
id: z.string(),
|
|
createdAt: z.string(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Type Exports
|
|
// ============================================================================
|
|
|
|
export type SupportCaseStatus = z.infer<typeof supportCaseStatusSchema>;
|
|
export type SupportCasePriority = z.infer<typeof supportCasePrioritySchema>;
|
|
export type SupportCaseCategory = z.infer<typeof supportCaseCategorySchema>;
|
|
export type SupportCase = z.infer<typeof supportCaseSchema>;
|
|
export type SupportCaseSummary = z.infer<typeof supportCaseSummarySchema>;
|
|
export type SupportCaseList = z.infer<typeof supportCaseListSchema>;
|
|
export type SupportCaseFilter = z.infer<typeof supportCaseFilterSchema>;
|
|
export type CreateCaseRequest = z.infer<typeof createCaseRequestSchema>;
|
|
export type CreateCaseResponse = z.infer<typeof createCaseResponseSchema>;
|
|
export type PublicContactRequest = z.infer<typeof publicContactRequestSchema>;
|
|
export type CaseMessageType = z.infer<typeof caseMessageTypeSchema>;
|
|
export type CaseMessageDirection = z.infer<typeof caseMessageDirectionSchema>;
|
|
export type CaseMessage = z.infer<typeof caseMessageSchema>;
|
|
export type CaseMessageList = z.infer<typeof caseMessageListSchema>;
|
|
export type AddCaseCommentRequest = z.infer<typeof addCaseCommentRequestSchema>;
|
|
export type AddCaseCommentResponse = z.infer<typeof addCaseCommentResponseSchema>;
|