- Added methods in `SalesforceCaseService` to fetch and add case messages, unifying email messages and case comments into a chronological conversation thread. - Enhanced `SupportService` and `SupportController` to handle case message retrieval and comment addition, integrating new DTOs for request and response validation. - Updated `SupportCaseDetailView` in the Portal to display conversation messages and allow users to add comments, improving user interaction with support cases. - Introduced new schemas in the domain for case messages and comments, ensuring robust validation and type safety. - Refactored Salesforce mappers to transform email messages and case comments into a unified format for display in the portal.
185 lines
6.1 KiB
TypeScript
185 lines
6.1 KiB
TypeScript
import { z } from "zod";
|
|
import { SUPPORT_CASE_STATUS, SUPPORT_CASE_PRIORITY, SUPPORT_CASE_CATEGORY } from "./contract.js";
|
|
|
|
/**
|
|
* Portal status values (mapped from Salesforce Japanese API names)
|
|
*/
|
|
const supportCaseStatusValues = [
|
|
SUPPORT_CASE_STATUS.NEW,
|
|
SUPPORT_CASE_STATUS.IN_PROGRESS,
|
|
SUPPORT_CASE_STATUS.AWAITING_APPROVAL,
|
|
SUPPORT_CASE_STATUS.VPN_PENDING,
|
|
SUPPORT_CASE_STATUS.PENDING,
|
|
SUPPORT_CASE_STATUS.RESOLVED,
|
|
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: z.string(), // Allow any status from Salesforce
|
|
priority: z.string(), // Allow any priority from Salesforce
|
|
category: z.string().nullable(), // Maps to Salesforce Type field
|
|
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: z.string().optional(),
|
|
priority: z.string().optional(),
|
|
category: z.string().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(),
|
|
});
|
|
|
|
/**
|
|
* 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>;
|