feat: build collaboration workspaces
This commit is contained in:
246
modules/family/types.ts
Normal file
246
modules/family/types.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
export const FAMILY_CONTACT_STATUS_VALUES = ["active", "suspended", "archived"] as const;
|
||||
export type FamilyContactStatus = (typeof FAMILY_CONTACT_STATUS_VALUES)[number];
|
||||
|
||||
export const FAMILY_VISIT_STATUS_VALUES = ["requested", "approved", "completed", "cancelled"] as const;
|
||||
export type FamilyVisitStatus = (typeof FAMILY_VISIT_STATUS_VALUES)[number];
|
||||
|
||||
export const FAMILY_FEEDBACK_TYPE_VALUES = ["service", "care", "meal", "environment", "other"] as const;
|
||||
export type FamilyFeedbackType = (typeof FAMILY_FEEDBACK_TYPE_VALUES)[number];
|
||||
|
||||
export const FAMILY_FEEDBACK_STATUS_VALUES = ["open", "in_progress", "resolved", "closed"] as const;
|
||||
export type FamilyFeedbackStatus = (typeof FAMILY_FEEDBACK_STATUS_VALUES)[number];
|
||||
|
||||
export const FAMILY_CONTACT_STATUS_LABELS: Record<FamilyContactStatus, string> = {
|
||||
active: "有效",
|
||||
suspended: "暂停",
|
||||
archived: "归档",
|
||||
};
|
||||
|
||||
export const FAMILY_VISIT_STATUS_LABELS: Record<FamilyVisitStatus, string> = {
|
||||
requested: "待确认",
|
||||
approved: "已确认",
|
||||
completed: "已完成",
|
||||
cancelled: "已取消",
|
||||
};
|
||||
|
||||
export const FAMILY_FEEDBACK_TYPE_LABELS: Record<FamilyFeedbackType, string> = {
|
||||
service: "服务",
|
||||
care: "照护",
|
||||
meal: "餐饮",
|
||||
environment: "环境",
|
||||
other: "其他",
|
||||
};
|
||||
|
||||
export const FAMILY_FEEDBACK_STATUS_LABELS: Record<FamilyFeedbackStatus, string> = {
|
||||
open: "待处理",
|
||||
in_progress: "处理中",
|
||||
resolved: "已解决",
|
||||
closed: "已关闭",
|
||||
};
|
||||
|
||||
export type FamilyContact = {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
elderId: string;
|
||||
elderName: string;
|
||||
name: string;
|
||||
relationship: string;
|
||||
phone: string;
|
||||
status: FamilyContactStatus;
|
||||
notes: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type FamilyVisitAppointment = {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
elderId: string;
|
||||
elderName: string;
|
||||
contactId?: string;
|
||||
contactName: string;
|
||||
scheduledAt: string;
|
||||
status: FamilyVisitStatus;
|
||||
notes: string;
|
||||
handledByAccountId?: string;
|
||||
handledByName?: string;
|
||||
handledAt?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type FamilyFeedback = {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
elderId: string;
|
||||
elderName: string;
|
||||
contactId?: string;
|
||||
contactName: string;
|
||||
feedbackType: FamilyFeedbackType;
|
||||
content: string;
|
||||
status: FamilyFeedbackStatus;
|
||||
responseNotes: string;
|
||||
handledByAccountId?: string;
|
||||
handledByName?: string;
|
||||
handledAt?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type FamilyServiceData = {
|
||||
metrics: {
|
||||
activeContacts: number;
|
||||
pendingVisits: number;
|
||||
openFeedback: number;
|
||||
resolvedFeedback: number;
|
||||
};
|
||||
elderOptions: Array<{ id: string; name: string }>;
|
||||
contacts: FamilyContact[];
|
||||
visits: FamilyVisitAppointment[];
|
||||
feedback: FamilyFeedback[];
|
||||
};
|
||||
|
||||
export type FamilyContactInput = {
|
||||
elderId: string;
|
||||
name: string;
|
||||
relationship: string;
|
||||
phone: string;
|
||||
status: FamilyContactStatus;
|
||||
notes: string;
|
||||
};
|
||||
|
||||
export type FamilyVisitInput = {
|
||||
elderId: string;
|
||||
contactId?: string;
|
||||
scheduledAt: string;
|
||||
status: FamilyVisitStatus;
|
||||
notes: string;
|
||||
};
|
||||
|
||||
export type FamilyFeedbackInput = {
|
||||
elderId: string;
|
||||
contactId?: string;
|
||||
feedbackType: FamilyFeedbackType;
|
||||
content: string;
|
||||
status: FamilyFeedbackStatus;
|
||||
responseNotes: string;
|
||||
};
|
||||
|
||||
type ValidationSuccess<T> = {
|
||||
success: true;
|
||||
data: T;
|
||||
};
|
||||
|
||||
type ValidationFailure = {
|
||||
success: false;
|
||||
reason: string;
|
||||
};
|
||||
|
||||
export type ValidationResult<T> = ValidationSuccess<T> | ValidationFailure;
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function readString(source: Record<string, unknown>, key: string): string {
|
||||
const value = source[key];
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function readOptionalString(source: Record<string, unknown>, key: string): string | undefined {
|
||||
const value = readString(source, key);
|
||||
return value || undefined;
|
||||
}
|
||||
|
||||
function readEnum<T extends string>(value: unknown, values: readonly T[]): T | null {
|
||||
return typeof value === "string" ? values.find((item) => item === value) ?? null : null;
|
||||
}
|
||||
|
||||
function validateRequiredDate(value: string, label: string): ValidationResult<string> {
|
||||
return value && !Number.isNaN(new Date(value).getTime())
|
||||
? { success: true, data: value }
|
||||
: { success: false, reason: `${label}无效` };
|
||||
}
|
||||
|
||||
export function validateFamilyContactInput(value: unknown): ValidationResult<FamilyContactInput> {
|
||||
if (!isRecord(value)) {
|
||||
return { success: false, reason: "请求数据格式无效" };
|
||||
}
|
||||
|
||||
const elderId = readString(value, "elderId");
|
||||
const name = readString(value, "name");
|
||||
const relationship = readString(value, "relationship");
|
||||
const phone = readString(value, "phone");
|
||||
if (!elderId || !name || !relationship || !phone) {
|
||||
return { success: false, reason: "老人、家属姓名、关系和手机号不能为空" };
|
||||
}
|
||||
|
||||
const status = readEnum(value.status, FAMILY_CONTACT_STATUS_VALUES);
|
||||
if (!status) {
|
||||
return { success: false, reason: "家属联系人状态无效" };
|
||||
}
|
||||
|
||||
return { success: true, data: { elderId, name, relationship, phone, status, notes: readString(value, "notes") } };
|
||||
}
|
||||
|
||||
export function validateFamilyVisitInput(value: unknown): ValidationResult<FamilyVisitInput> {
|
||||
if (!isRecord(value)) {
|
||||
return { success: false, reason: "请求数据格式无效" };
|
||||
}
|
||||
|
||||
const elderId = readString(value, "elderId");
|
||||
if (!elderId) {
|
||||
return { success: false, reason: "探访预约必须选择老人" };
|
||||
}
|
||||
|
||||
const scheduledAt = validateRequiredDate(readString(value, "scheduledAt"), "预约时间");
|
||||
if (!scheduledAt.success) {
|
||||
return scheduledAt;
|
||||
}
|
||||
|
||||
const status = readEnum(value.status, FAMILY_VISIT_STATUS_VALUES);
|
||||
if (!status) {
|
||||
return { success: false, reason: "探访预约状态无效" };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
elderId,
|
||||
contactId: readOptionalString(value, "contactId"),
|
||||
scheduledAt: scheduledAt.data,
|
||||
status,
|
||||
notes: readString(value, "notes"),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function validateFamilyFeedbackInput(value: unknown): ValidationResult<FamilyFeedbackInput> {
|
||||
if (!isRecord(value)) {
|
||||
return { success: false, reason: "请求数据格式无效" };
|
||||
}
|
||||
|
||||
const elderId = readString(value, "elderId");
|
||||
const content = readString(value, "content");
|
||||
if (!elderId || !content) {
|
||||
return { success: false, reason: "反馈必须选择老人并填写内容" };
|
||||
}
|
||||
|
||||
const feedbackType = readEnum(value.feedbackType, FAMILY_FEEDBACK_TYPE_VALUES);
|
||||
const status = readEnum(value.status, FAMILY_FEEDBACK_STATUS_VALUES);
|
||||
if (!feedbackType || !status) {
|
||||
return { success: false, reason: "反馈类型或状态无效" };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
elderId,
|
||||
contactId: readOptionalString(value, "contactId"),
|
||||
feedbackType,
|
||||
content,
|
||||
status,
|
||||
responseNotes: readString(value, "responseNotes"),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user