139 lines
6.2 KiB
TypeScript
139 lines
6.2 KiB
TypeScript
import {
|
|
mysqlTable,
|
|
mysqlEnum,
|
|
serial,
|
|
varchar,
|
|
text,
|
|
timestamp,
|
|
bigint,
|
|
int,
|
|
decimal,
|
|
index,
|
|
} from "drizzle-orm/mysql-core";
|
|
|
|
// ── Users (base auth table) ──────────────────────────────────
|
|
export const users = mysqlTable("users", {
|
|
id: serial("id").primaryKey(),
|
|
unionId: varchar("unionId", { length: 255 }).notNull().unique(),
|
|
name: varchar("name", { length: 255 }),
|
|
email: varchar("email", { length: 320 }),
|
|
passwordHash: text("password_hash"),
|
|
avatar: text("avatar"),
|
|
role: mysqlEnum("role", ["admin", "officer", "manager"]).default("manager").notNull(),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().notNull().$onUpdate(() => new Date()),
|
|
lastSignInAt: timestamp("lastSignInAt").defaultNow().notNull(),
|
|
});
|
|
|
|
export type User = typeof users.$inferSelect;
|
|
export type InsertUser = typeof users.$inferInsert;
|
|
|
|
// ── Places of Worship ─────────────────────────────────────────
|
|
export const placesOfWorship = mysqlTable(
|
|
"places_of_worship",
|
|
{
|
|
id: serial("id").primaryKey(),
|
|
name: varchar("name", { length: 255 }).notNull(),
|
|
type: mysqlEnum("type", ["mosque", "church", "temple", "vihara", "other"]).notNull(),
|
|
address: text("address"),
|
|
latitude: decimal("latitude", { precision: 10, scale: 8 }).notNull(),
|
|
longitude: decimal("longitude", { precision: 11, scale: 8 }).notNull(),
|
|
radius: int("radius").default(1000).notNull(), // radius in meters
|
|
capacity: int("capacity").default(100).notNull(), // max beneficiaries
|
|
contactName: varchar("contact_name", { length: 255 }),
|
|
contactPhone: varchar("contact_phone", { length: 20 }),
|
|
managerId: bigint("manager_id", { mode: "number", unsigned: true }),
|
|
isActive: mysqlEnum("is_active", ["yes", "no"]).default("yes").notNull(),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").defaultNow().notNull().$onUpdate(() => new Date()),
|
|
},
|
|
(table) => ({
|
|
typeIdx: index("pow_type_idx").on(table.type),
|
|
managerIdx: index("pow_manager_idx").on(table.managerId),
|
|
})
|
|
);
|
|
|
|
export type PlaceOfWorship = typeof placesOfWorship.$inferSelect;
|
|
export type InsertPlaceOfWorship = typeof placesOfWorship.$inferInsert;
|
|
|
|
// ── Recipients (beneficiaries) ─────────────────────────────────
|
|
export const recipients = mysqlTable(
|
|
"recipients",
|
|
{
|
|
id: serial("id").primaryKey(),
|
|
nik: varchar("nik", { length: 16 }).notNull().unique(),
|
|
name: varchar("name", { length: 255 }).notNull(),
|
|
birthDate: varchar("birth_date", { length: 10 }),
|
|
gender: mysqlEnum("gender", ["male", "female"]),
|
|
address: text("address"),
|
|
phone: varchar("phone", { length: 15 }),
|
|
latitude: decimal("latitude", { precision: 10, scale: 8 }),
|
|
longitude: decimal("longitude", { precision: 11, scale: 8 }),
|
|
familyMembers: int("family_members").default(1),
|
|
incomePerMonth: int("income_per_month").default(0),
|
|
status: mysqlEnum("status", ["pending", "active", "rejected", "suspended"]).default("pending").notNull(),
|
|
rejectionReason: text("rejection_reason"),
|
|
placeOfWorshipId: bigint("place_of_worship_id", { mode: "number", unsigned: true }).notNull(),
|
|
registeredBy: bigint("registered_by", { mode: "number", unsigned: true }),
|
|
verifiedBy: bigint("verified_by", { mode: "number", unsigned: true }),
|
|
verifiedAt: timestamp("verified_at"),
|
|
notes: text("notes"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
updatedAt: timestamp("updated_at").defaultNow().notNull().$onUpdate(() => new Date()),
|
|
},
|
|
(table) => ({
|
|
nikIdx: index("rec_nik_idx").on(table.nik),
|
|
statusIdx: index("rec_status_idx").on(table.status),
|
|
powIdx: index("rec_pow_idx").on(table.placeOfWorshipId),
|
|
})
|
|
);
|
|
|
|
export type Recipient = typeof recipients.$inferSelect;
|
|
export type InsertRecipient = typeof recipients.$inferInsert;
|
|
|
|
// ── Distributions ──────────────────────────────────────────────
|
|
export const distributions = mysqlTable(
|
|
"distributions",
|
|
{
|
|
id: serial("id").primaryKey(),
|
|
placeOfWorshipId: bigint("place_of_worship_id", { mode: "number", unsigned: true }).notNull(),
|
|
recipientId: bigint("recipient_id", { mode: "number", unsigned: true }).notNull(),
|
|
distributedBy: bigint("distributed_by", { mode: "number", unsigned: true }),
|
|
aidType: varchar("aid_type", { length: 100 }).notNull(),
|
|
amount: decimal("amount", { precision: 12, scale: 2 }).default("0.00"),
|
|
quantity: int("quantity").default(1),
|
|
unit: varchar("unit", { length: 50 }).default("package"),
|
|
distributionDate: timestamp("distribution_date").defaultNow().notNull(),
|
|
notes: text("notes"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
},
|
|
(table) => ({
|
|
powIdx: index("dist_pow_idx").on(table.placeOfWorshipId),
|
|
recIdx: index("dist_rec_idx").on(table.recipientId),
|
|
})
|
|
);
|
|
|
|
export type Distribution = typeof distributions.$inferSelect;
|
|
export type InsertDistribution = typeof distributions.$inferInsert;
|
|
|
|
// ── Activity Logs ──────────────────────────────────────────────
|
|
export const activityLogs = mysqlTable(
|
|
"activity_logs",
|
|
{
|
|
id: serial("id").primaryKey(),
|
|
userId: bigint("user_id", { mode: "number", unsigned: true }),
|
|
action: varchar("action", { length: 50 }).notNull(), // e.g. "CREATE", "UPDATE", "DELETE", "VERIFY"
|
|
entityType: varchar("entity_type", { length: 50 }).notNull(), // "recipient", "place_of_worship", "distribution"
|
|
entityId: bigint("entity_id", { mode: "number", unsigned: true }),
|
|
details: text("details"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
},
|
|
(table) => ({
|
|
userIdx: index("act_user_idx").on(table.userId),
|
|
entityIdx: index("act_entity_idx").on(table.entityType, table.entityId),
|
|
})
|
|
);
|
|
|
|
export type ActivityLog = typeof activityLogs.$inferSelect;
|
|
export type InsertActivityLog = typeof activityLogs.$inferInsert;
|