2025-11-18 14:06:27 +09:00
|
|
|
import { z } from "zod";
|
2025-12-25 17:30:02 +09:00
|
|
|
import { SUPPORT_CASE_STATUS, SUPPORT_CASE_PRIORITY, SUPPORT_CASE_CATEGORY } from "./contract.js";
|
2025-11-18 14:06:27 +09:00
|
|
|
|
2025-11-26 16:36:06 +09:00
|
|
|
/**
|
2026-01-05 15:11:56 +09:00
|
|
|
* 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
|
2025-11-26 16:36:06 +09:00
|
|
|
*/
|
2025-11-18 14:06:27 +09:00
|
|
|
const supportCaseStatusValues = [
|
2025-11-26 16:36:06 +09:00
|
|
|
SUPPORT_CASE_STATUS.NEW,
|
2025-11-18 14:06:27 +09:00
|
|
|
SUPPORT_CASE_STATUS.IN_PROGRESS,
|
2026-01-05 15:11:56 +09:00
|
|
|
SUPPORT_CASE_STATUS.AWAITING_CUSTOMER,
|
2025-11-18 14:06:27 +09:00
|
|
|
SUPPORT_CASE_STATUS.CLOSED,
|
|
|
|
|
] as const;
|
|
|
|
|
|
2025-11-26 16:36:06 +09:00
|
|
|
/**
|
|
|
|
|
* Portal priority values (mapped from Salesforce Japanese API names)
|
|
|
|
|
*/
|
2025-11-18 14:06:27 +09:00
|
|
|
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);
|
|
|
|
|
|
2025-11-26 16:36:06 +09:00
|
|
|
/**
|
|
|
|
|
* Support case schema - compatible with Salesforce Case object
|
|
|
|
|
* ID is a string (Salesforce ID format: 15 or 18 char alphanumeric)
|
|
|
|
|
*/
|
2025-11-18 14:06:27 +09:00
|
|
|
export const supportCaseSchema = z.object({
|
2025-11-26 16:36:06 +09:00
|
|
|
id: z.string().min(15).max(18),
|
|
|
|
|
caseNumber: z.string(),
|
2025-11-18 14:06:27 +09:00
|
|
|
subject: z.string().min(1),
|
2026-02-25 11:30:02 +09:00
|
|
|
status: supportCaseStatusSchema,
|
|
|
|
|
priority: supportCasePrioritySchema,
|
|
|
|
|
category: supportCaseCategorySchema.nullable(),
|
2025-11-18 14:06:27 +09:00
|
|
|
createdAt: z.string(),
|
|
|
|
|
updatedAt: z.string(),
|
2025-11-26 16:36:06 +09:00
|
|
|
closedAt: z.string().nullable(),
|
2025-11-18 14:06:27 +09:00
|
|
|
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({
|
2026-02-25 11:30:02 +09:00
|
|
|
status: supportCaseStatusSchema.optional(),
|
|
|
|
|
priority: supportCasePrioritySchema.optional(),
|
|
|
|
|
category: supportCaseCategorySchema.optional(),
|
2025-11-18 14:06:27 +09:00
|
|
|
search: z.string().trim().min(1).optional(),
|
|
|
|
|
})
|
|
|
|
|
.default({});
|
|
|
|
|
|
2025-11-26 16:36:06 +09:00
|
|
|
/**
|
|
|
|
|
* 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(),
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-25 19:01:00 +09:00
|
|
|
/**
|
|
|
|
|
* 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"),
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-29 18:39:13 +09:00
|
|
|
// ============================================================================
|
|
|
|
|
// 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(),
|
2026-01-05 15:11:56 +09:00
|
|
|
/** Whether the message has attachments (for emails) */
|
|
|
|
|
hasAttachment: z.boolean().optional(),
|
2025-12-29 18:39:13 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
2025-11-18 14:06:27 +09:00
|
|
|
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>;
|
2025-11-26 16:36:06 +09:00
|
|
|
export type CreateCaseRequest = z.infer<typeof createCaseRequestSchema>;
|
|
|
|
|
export type CreateCaseResponse = z.infer<typeof createCaseResponseSchema>;
|
2025-12-25 19:01:00 +09:00
|
|
|
export type PublicContactRequest = z.infer<typeof publicContactRequestSchema>;
|
2025-12-29 18:39:13 +09:00
|
|
|
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>;
|