Add executive meeting management system with role-based access
Implement a new API endpoint and UI for managing executive meetings, including role-based access control and database schema changes for meeting data and attendees. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 079afca5-8c32-40ea-b7d1-8bf26358ff75 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/BRoHDEI Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
pgTable,
|
||||
serial,
|
||||
integer,
|
||||
varchar,
|
||||
text,
|
||||
timestamp,
|
||||
date,
|
||||
time,
|
||||
jsonb,
|
||||
index,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { usersTable } from "./users";
|
||||
|
||||
export const executiveMeetingsTable = pgTable(
|
||||
"executive_meetings",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
dailyNumber: integer("daily_number").notNull(),
|
||||
titleAr: varchar("title_ar", { length: 300 }).notNull(),
|
||||
titleEn: varchar("title_en", { length: 300 }).notNull().default(""),
|
||||
meetingDate: date("meeting_date").notNull(),
|
||||
startTime: time("start_time", { withTimezone: false }),
|
||||
endTime: time("end_time", { withTimezone: false }),
|
||||
location: varchar("location", { length: 300 }),
|
||||
meetingUrl: text("meeting_url"),
|
||||
platform: varchar("platform", { length: 32 }).notNull().default("none"),
|
||||
status: varchar("status", { length: 32 }).notNull().default("scheduled"),
|
||||
isHighlighted: integer("is_highlighted").notNull().default(0),
|
||||
notes: text("notes"),
|
||||
attachments: jsonb("attachments").default([]),
|
||||
createdBy: integer("created_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
updatedBy: integer("updated_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow()
|
||||
.$onUpdate(() => new Date()),
|
||||
},
|
||||
(t) => ({
|
||||
dateIdx: index("executive_meetings_date_idx").on(t.meetingDate),
|
||||
dateNumberUnique: uniqueIndex("executive_meetings_date_number_unique").on(
|
||||
t.meetingDate,
|
||||
t.dailyNumber,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const executiveMeetingAttendeesTable = pgTable(
|
||||
"executive_meeting_attendees",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
meetingId: integer("meeting_id")
|
||||
.notNull()
|
||||
.references(() => executiveMeetingsTable.id, { onDelete: "cascade" }),
|
||||
name: varchar("name", { length: 200 }).notNull(),
|
||||
title: varchar("title", { length: 200 }),
|
||||
attendanceType: varchar("attendance_type", { length: 32 })
|
||||
.notNull()
|
||||
.default("internal"),
|
||||
sortOrder: integer("sort_order").notNull().default(0),
|
||||
},
|
||||
(t) => ({
|
||||
meetingIdx: index("executive_meeting_attendees_meeting_idx").on(t.meetingId),
|
||||
}),
|
||||
);
|
||||
|
||||
export const executiveMeetingRequestsTable = pgTable(
|
||||
"executive_meeting_requests",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
meetingId: integer("meeting_id")
|
||||
.notNull()
|
||||
.references(() => executiveMeetingsTable.id, { onDelete: "cascade" }),
|
||||
requestedBy: integer("requested_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
requestType: varchar("request_type", { length: 64 }).notNull(),
|
||||
requestDetails: jsonb("request_details"),
|
||||
status: varchar("status", { length: 32 }).notNull().default("new"),
|
||||
reviewedBy: integer("reviewed_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
reviewDecision: varchar("review_decision", { length: 32 }),
|
||||
reviewNotes: text("review_notes"),
|
||||
assignedTo: integer("assigned_to").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow()
|
||||
.$onUpdate(() => new Date()),
|
||||
},
|
||||
);
|
||||
|
||||
export const executiveMeetingTasksTable = pgTable("executive_meeting_tasks", {
|
||||
id: serial("id").primaryKey(),
|
||||
requestId: integer("request_id").references(() => executiveMeetingRequestsTable.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
meetingId: integer("meeting_id").references(() => executiveMeetingsTable.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
assignedTo: integer("assigned_to").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
taskType: varchar("task_type", { length: 64 }).notNull(),
|
||||
status: varchar("status", { length: 32 }).notNull().default("pending"),
|
||||
dueAt: timestamp("due_at", { withTimezone: true }),
|
||||
completedAt: timestamp("completed_at", { withTimezone: true }),
|
||||
notes: text("notes"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow()
|
||||
.$onUpdate(() => new Date()),
|
||||
});
|
||||
|
||||
export const executiveMeetingNotificationsTable = pgTable(
|
||||
"executive_meeting_notifications",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
meetingId: integer("meeting_id").references(() => executiveMeetingsTable.id, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
userId: integer("user_id").references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
notificationType: varchar("notification_type", { length: 64 }).notNull(),
|
||||
scheduledAt: timestamp("scheduled_at", { withTimezone: true }),
|
||||
sentAt: timestamp("sent_at", { withTimezone: true }),
|
||||
status: varchar("status", { length: 32 }).notNull().default("pending"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
);
|
||||
|
||||
export const executiveMeetingAuditLogsTable = pgTable(
|
||||
"executive_meeting_audit_logs",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
action: varchar("action", { length: 100 }).notNull(),
|
||||
entityType: varchar("entity_type", { length: 50 }).notNull(),
|
||||
entityId: integer("entity_id"),
|
||||
oldValue: jsonb("old_value"),
|
||||
newValue: jsonb("new_value"),
|
||||
performedBy: integer("performed_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
performedAt: timestamp("performed_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
);
|
||||
|
||||
export const executiveMeetingPdfArchivesTable = pgTable(
|
||||
"executive_meeting_pdf_archives",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
archiveDate: date("archive_date").notNull(),
|
||||
filePath: text("file_path").notNull(),
|
||||
version: integer("version").notNull().default(1),
|
||||
generatedBy: integer("generated_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
generatedAt: timestamp("generated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
);
|
||||
|
||||
export const executiveMeetingFontSettingsTable = pgTable(
|
||||
"executive_meeting_font_settings",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
scope: varchar("scope", { length: 16 }).notNull().default("global"),
|
||||
userId: integer("user_id").references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
fontFamily: varchar("font_family", { length: 100 }).notNull().default("system"),
|
||||
fontSize: integer("font_size").notNull().default(14),
|
||||
fontWeight: varchar("font_weight", { length: 16 }).notNull().default("regular"),
|
||||
alignment: varchar("alignment", { length: 16 }).notNull().default("start"),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow()
|
||||
.$onUpdate(() => new Date()),
|
||||
},
|
||||
);
|
||||
|
||||
export type ExecutiveMeeting = typeof executiveMeetingsTable.$inferSelect;
|
||||
export type ExecutiveMeetingAttendee = typeof executiveMeetingAttendeesTable.$inferSelect;
|
||||
export type ExecutiveMeetingRequest = typeof executiveMeetingRequestsTable.$inferSelect;
|
||||
export type ExecutiveMeetingTask = typeof executiveMeetingTasksTable.$inferSelect;
|
||||
@@ -12,3 +12,4 @@ export * from "./password-reset-tokens";
|
||||
export * from "./notes";
|
||||
export * from "./groups";
|
||||
export * from "./audit-logs";
|
||||
export * from "./executive-meetings";
|
||||
|
||||
Reference in New Issue
Block a user