127 lines
5.9 KiB
TypeScript
127 lines
5.9 KiB
TypeScript
import {
|
|
int,
|
|
mysqlEnum,
|
|
mysqlTable,
|
|
text,
|
|
timestamp,
|
|
varchar,
|
|
boolean,
|
|
bigint,
|
|
float,
|
|
json,
|
|
} from "drizzle-orm/mysql-core";
|
|
|
|
// ─── Users (médecins) ────────────────────────────────────────────────────────
|
|
export const users = mysqlTable("users", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
openId: varchar("openId", { length: 64 }).notNull().unique(),
|
|
name: text("name"),
|
|
email: varchar("email", { length: 320 }),
|
|
loginMethod: varchar("loginMethod", { length: 64 }),
|
|
role: mysqlEnum("role", ["user", "admin"]).default("user").notNull(),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
|
lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(),
|
|
});
|
|
|
|
export type User = typeof users.$inferSelect;
|
|
export type InsertUser = typeof users.$inferInsert;
|
|
|
|
// ─── Subscriptions ───────────────────────────────────────────────────────────
|
|
export const subscriptions = mysqlTable("subscriptions", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
userId: int("userId").notNull(),
|
|
stripeCustomerId: varchar("stripeCustomerId", { length: 128 }),
|
|
stripeSubscriptionId: varchar("stripeSubscriptionId", { length: 128 }),
|
|
stripePriceId: varchar("stripePriceId", { length: 128 }),
|
|
plan: mysqlEnum("plan", ["trial", "basic", "pro"]).default("trial").notNull(),
|
|
status: mysqlEnum("status", ["trialing", "active", "past_due", "canceled", "expired"]).default("trialing").notNull(),
|
|
trialStartedAt: timestamp("trialStartedAt").defaultNow().notNull(),
|
|
trialEndsAt: timestamp("trialEndsAt").notNull(),
|
|
currentPeriodStart: timestamp("currentPeriodStart"),
|
|
currentPeriodEnd: timestamp("currentPeriodEnd"),
|
|
canceledAt: timestamp("canceledAt"),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
|
});
|
|
|
|
export type Subscription = typeof subscriptions.$inferSelect;
|
|
export type InsertSubscription = typeof subscriptions.$inferInsert;
|
|
|
|
// ─── Clinics (cabinets médicaux) ─────────────────────────────────────────────
|
|
export const clinics = mysqlTable("clinics", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
userId: int("userId").notNull(),
|
|
name: varchar("name", { length: 255 }).notNull(),
|
|
address: text("address"),
|
|
phone: varchar("phone", { length: 32 }),
|
|
color: varchar("color", { length: 16 }).default("#0d9488"),
|
|
isActive: boolean("isActive").default(true).notNull(),
|
|
// QR code token rotatif anti-triche
|
|
qrToken: varchar("qrToken", { length: 64 }).notNull(),
|
|
qrTokenExpiresAt: timestamp("qrTokenExpiresAt"),
|
|
qrRotationMinutes: int("qrRotationMinutes").default(30),
|
|
// Paramètres file d'attente
|
|
avgConsultationMinutes: int("avgConsultationMinutes").default(15),
|
|
maxQueueSize: int("maxQueueSize").default(50),
|
|
isQueueOpen: boolean("isQueueOpen").default(false).notNull(),
|
|
currentTicketNumber: int("currentTicketNumber").default(0).notNull(),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
|
});
|
|
|
|
export type Clinic = typeof clinics.$inferSelect;
|
|
export type InsertClinic = typeof clinics.$inferInsert;
|
|
|
|
// ─── Queue Entries (patients en file) ────────────────────────────────────────
|
|
export const queueEntries = mysqlTable("queue_entries", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
clinicId: int("clinicId").notNull(),
|
|
ticketNumber: int("ticketNumber").notNull(),
|
|
// Identifiant de session anonyme du patient
|
|
patientToken: varchar("patientToken", { length: 64 }).notNull(),
|
|
patientName: varchar("patientName", { length: 128 }),
|
|
patientPhone: varchar("patientPhone", { length: 32 }),
|
|
status: mysqlEnum("status", ["waiting", "called", "in_consultation", "done", "absent", "canceled"])
|
|
.default("waiting")
|
|
.notNull(),
|
|
position: int("position").notNull(),
|
|
joinedAt: timestamp("joinedAt").defaultNow().notNull(),
|
|
calledAt: timestamp("calledAt"),
|
|
consultationStartAt: timestamp("consultationStartAt"),
|
|
consultationEndAt: timestamp("consultationEndAt"),
|
|
estimatedWaitMinutes: int("estimatedWaitMinutes"),
|
|
notificationSent: boolean("notificationSent").default(false).notNull(),
|
|
// Pour l'impression de ticket
|
|
isPrinted: boolean("isPrinted").default(false).notNull(),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
|
});
|
|
|
|
export type QueueEntry = typeof queueEntries.$inferSelect;
|
|
export type InsertQueueEntry = typeof queueEntries.$inferInsert;
|
|
|
|
// ─── Analytics Events ─────────────────────────────────────────────────────────
|
|
export const analyticsEvents = mysqlTable("analytics_events", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
clinicId: int("clinicId").notNull(),
|
|
eventType: mysqlEnum("eventType", [
|
|
"patient_joined",
|
|
"patient_called",
|
|
"patient_done",
|
|
"patient_absent",
|
|
"queue_opened",
|
|
"queue_closed",
|
|
]).notNull(),
|
|
ticketNumber: int("ticketNumber"),
|
|
waitMinutes: int("waitMinutes"),
|
|
consultationMinutes: int("consultationMinutes"),
|
|
queueSizeAtEvent: int("queueSizeAtEvent"),
|
|
hourOfDay: int("hourOfDay"),
|
|
dayOfWeek: int("dayOfWeek"),
|
|
metadata: json("metadata"),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
});
|
|
|
|
export type AnalyticsEvent = typeof analyticsEvents.$inferSelect;
|
|
export type InsertAnalyticsEvent = typeof analyticsEvents.$inferInsert;
|