feat: build collaboration workspaces

This commit is contained in:
2026-07-03 03:02:36 -07:00
parent bf3dd256ab
commit 4ba2a11b88
53 changed files with 9557 additions and 29 deletions

View File

@@ -30,6 +30,17 @@ export const healthReviewStatusEnum = pgEnum("health_review_status", ["pending",
export const careTaskTypeEnum = pgEnum("care_task_type", ["daily_care", "meal", "medication", "rehab", "inspection", "cleaning", "other"]);
export const careTaskPriorityEnum = pgEnum("care_task_priority", ["low", "normal", "high", "urgent"]);
export const careTaskStatusEnum = pgEnum("care_task_status", ["pending", "in_progress", "completed", "cancelled"]);
export const deviceAssetStatusEnum = pgEnum("device_asset_status", ["active", "maintenance", "disabled", "retired"]);
export const maintenanceTicketStatusEnum = pgEnum("maintenance_ticket_status", ["open", "assigned", "resolved", "closed", "cancelled"]);
export const maintenanceTicketPriorityEnum = pgEnum("maintenance_ticket_priority", ["low", "normal", "high", "urgent"]);
export const noticeStatusEnum = pgEnum("notice_status", ["draft", "published", "retracted"]);
export const alertRuleTypeEnum = pgEnum("alert_rule_type", ["health", "care", "device", "incident", "admission", "other"]);
export const alertRuleStatusEnum = pgEnum("alert_rule_status", ["enabled", "disabled"]);
export const alertTriggerStatusEnum = pgEnum("alert_trigger_status", ["open", "acknowledged", "resolved", "closed"]);
export const familyContactStatusEnum = pgEnum("family_contact_status", ["active", "suspended", "archived"]);
export const familyVisitStatusEnum = pgEnum("family_visit_status", ["requested", "approved", "completed", "cancelled"]);
export const familyFeedbackTypeEnum = pgEnum("family_feedback_type", ["service", "care", "meal", "environment", "other"]);
export const familyFeedbackStatusEnum = pgEnum("family_feedback_status", ["open", "in_progress", "resolved", "closed"]);
export const systemSettings = pgTable("system_settings", {
id: text("id").primaryKey().default("global"),
@@ -323,6 +334,150 @@ export const careTasks = pgTable("care_tasks", {
scheduleLookup: index("care_tasks_schedule_lookup").on(table.organizationId, table.scheduledAt),
}));
export const deviceAssets = pgTable("device_assets", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
name: text("name").notNull(),
code: text("code").notNull(),
category: text("category").notNull().default(""),
location: text("location").notNull().default(""),
status: deviceAssetStatusEnum("status").notNull().default("active"),
lastInspectedAt: timestamp("last_inspected_at", { withTimezone: true }),
notes: text("notes").notNull().default(""),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
codeUnique: uniqueIndex("device_assets_code_organization_unique").on(table.organizationId, table.code),
statusLookup: index("device_assets_status_lookup").on(table.organizationId, table.status),
}));
export const maintenanceTickets = pgTable("maintenance_tickets", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
deviceId: uuid("device_id").references(() => deviceAssets.id, { onDelete: "set null" }),
title: text("title").notNull(),
description: text("description").notNull().default(""),
priority: maintenanceTicketPriorityEnum("priority").notNull().default("normal"),
status: maintenanceTicketStatusEnum("status").notNull().default("open"),
assigneeLabel: text("assignee_label").notNull().default(""),
resolutionNotes: text("resolution_notes").notNull().default(""),
resolvedAt: timestamp("resolved_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
queueLookup: index("maintenance_tickets_queue_lookup").on(table.organizationId, table.status, table.priority),
deviceLookup: index("maintenance_tickets_device_lookup").on(table.organizationId, table.deviceId),
}));
export const notices = pgTable("notices", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
title: text("title").notNull(),
content: text("content").notNull().default(""),
audience: text("audience").notNull().default("全体员工"),
status: noticeStatusEnum("status").notNull().default("draft"),
publishedAt: timestamp("published_at", { withTimezone: true }),
createdByAccountId: uuid("created_by_account_id").references(() => accounts.id),
updatedByAccountId: uuid("updated_by_account_id").references(() => accounts.id),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
statusLookup: index("notices_status_lookup").on(table.organizationId, table.status),
}));
export const noticeReadReceipts = pgTable("notice_read_receipts", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
noticeId: uuid("notice_id").notNull().references(() => notices.id, { onDelete: "cascade" }),
accountId: uuid("account_id").notNull().references(() => accounts.id, { onDelete: "cascade" }),
readAt: timestamp("read_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
accountNoticeUnique: uniqueIndex("notice_read_receipts_account_notice_unique").on(table.noticeId, table.accountId),
noticeLookup: index("notice_read_receipts_notice_lookup").on(table.organizationId, table.noticeId),
}));
export const alertRules = pgTable("alert_rules", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
name: text("name").notNull(),
ruleType: alertRuleTypeEnum("rule_type").notNull().default("other"),
severity: incidentSeverityEnum("severity").notNull().default("warning"),
status: alertRuleStatusEnum("status").notNull().default("enabled"),
conditionSummary: text("condition_summary").notNull().default(""),
suggestion: text("suggestion").notNull().default(""),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
statusLookup: index("alert_rules_status_lookup").on(table.organizationId, table.status, table.ruleType),
}));
export const alertTriggers = pgTable("alert_triggers", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
ruleId: uuid("rule_id").references(() => alertRules.id, { onDelete: "set null" }),
elderId: uuid("elder_id").references(() => elders.id, { onDelete: "set null" }),
title: text("title").notNull(),
description: text("description").notNull().default(""),
status: alertTriggerStatusEnum("status").notNull().default("open"),
source: text("source").notNull().default(""),
handledByAccountId: uuid("handled_by_account_id").references(() => accounts.id),
handledAt: timestamp("handled_at", { withTimezone: true }),
handlingNotes: text("handling_notes").notNull().default(""),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
queueLookup: index("alert_triggers_queue_lookup").on(table.organizationId, table.status),
ruleLookup: index("alert_triggers_rule_lookup").on(table.organizationId, table.ruleId),
}));
export const familyContacts = pgTable("family_contacts", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
elderId: uuid("elder_id").notNull().references(() => elders.id, { onDelete: "cascade" }),
name: text("name").notNull(),
relationship: text("relationship").notNull(),
phone: text("phone").notNull(),
status: familyContactStatusEnum("status").notNull().default("active"),
notes: text("notes").notNull().default(""),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
elderLookup: index("family_contacts_elder_lookup").on(table.organizationId, table.elderId, table.status),
}));
export const familyVisitAppointments = pgTable("family_visit_appointments", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
elderId: uuid("elder_id").notNull().references(() => elders.id, { onDelete: "cascade" }),
contactId: uuid("contact_id").references(() => familyContacts.id, { onDelete: "set null" }),
scheduledAt: timestamp("scheduled_at", { withTimezone: true }).notNull(),
status: familyVisitStatusEnum("status").notNull().default("requested"),
notes: text("notes").notNull().default(""),
handledByAccountId: uuid("handled_by_account_id").references(() => accounts.id),
handledAt: timestamp("handled_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
scheduleLookup: index("family_visit_appointments_schedule_lookup").on(table.organizationId, table.status, table.scheduledAt),
}));
export const familyFeedback = pgTable("family_feedback", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
elderId: uuid("elder_id").notNull().references(() => elders.id, { onDelete: "cascade" }),
contactId: uuid("contact_id").references(() => familyContacts.id, { onDelete: "set null" }),
feedbackType: familyFeedbackTypeEnum("feedback_type").notNull().default("other"),
content: text("content").notNull(),
status: familyFeedbackStatusEnum("status").notNull().default("open"),
responseNotes: text("response_notes").notNull().default(""),
handledByAccountId: uuid("handled_by_account_id").references(() => accounts.id),
handledAt: timestamp("handled_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
queueLookup: index("family_feedback_queue_lookup").on(table.organizationId, table.status, table.feedbackType),
}));
export const admissions = pgTable("admissions", {
id: uuid("id").primaryKey().defaultRandom(),
organizationId: uuid("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),