Add functionality to manage visit photos and albums for protocols
Introduce new database tables for photo albums and photos, add API endpoints for CRUD operations on albums and photos, and implement frontend components for managing photo albums. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: ad3facca-9626-4e19-95c0-125e6d3bc7e2 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/1OXOhLE Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -241,6 +241,67 @@ export const protocolGiftIssuesTable = pgTable(
|
||||
}),
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Visit photo albums (صور الزيارات) — "تصوير" tab.
|
||||
//
|
||||
// An album groups photographs of a visit. It may optionally be linked to an
|
||||
// external protocol meeting (اللقاءات الخارجية); the link is `set null` so
|
||||
// deleting the meeting never removes the album. The album cover is derived at
|
||||
// query time from the first photo (by sortOrder), so there is no circular
|
||||
// album <-> photo foreign key to keep in sync.
|
||||
// ---------------------------------------------------------------------------
|
||||
export const protocolPhotoAlbumsTable = pgTable(
|
||||
"protocol_photo_albums",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
name: varchar("name", { length: 300 }).notNull(),
|
||||
externalMeetingId: integer("external_meeting_id").references(
|
||||
() => protocolExternalMeetingsTable.id,
|
||||
{ onDelete: "set null" },
|
||||
),
|
||||
createdBy: integer("created_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) => ({
|
||||
meetingIdx: index("protocol_albums_meeting_idx").on(t.externalMeetingId),
|
||||
}),
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Photos inside an album. Each photo references a private object-storage path
|
||||
// (`/objects/<id>`) served through the authorized object endpoint. Deleting an
|
||||
// album cascades to its photos.
|
||||
// ---------------------------------------------------------------------------
|
||||
export const protocolPhotosTable = pgTable(
|
||||
"protocol_photos",
|
||||
{
|
||||
id: serial("id").primaryKey(),
|
||||
albumId: integer("album_id")
|
||||
.notNull()
|
||||
.references(() => protocolPhotoAlbumsTable.id, { onDelete: "cascade" }),
|
||||
objectPath: varchar("object_path", { length: 500 }).notNull(),
|
||||
sortOrder: integer("sort_order").notNull().default(0),
|
||||
uploadedBy: integer("uploaded_by").references(() => usersTable.id, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(t) => ({
|
||||
albumIdx: index("protocol_photos_album_idx").on(t.albumId),
|
||||
pathIdx: index("protocol_photos_path_idx").on(t.objectPath),
|
||||
}),
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Audit log (scoped to the protocol module only)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -306,4 +367,18 @@ export type InsertProtocolGiftIssue = z.infer<
|
||||
>;
|
||||
export type ProtocolGiftIssue = typeof protocolGiftIssuesTable.$inferSelect;
|
||||
|
||||
export const insertProtocolPhotoAlbumSchema = createInsertSchema(
|
||||
protocolPhotoAlbumsTable,
|
||||
).omit({ id: true, createdAt: true, updatedAt: true });
|
||||
export type InsertProtocolPhotoAlbum = z.infer<
|
||||
typeof insertProtocolPhotoAlbumSchema
|
||||
>;
|
||||
export type ProtocolPhotoAlbum = typeof protocolPhotoAlbumsTable.$inferSelect;
|
||||
|
||||
export const insertProtocolPhotoSchema = createInsertSchema(
|
||||
protocolPhotosTable,
|
||||
).omit({ id: true, createdAt: true });
|
||||
export type InsertProtocolPhoto = z.infer<typeof insertProtocolPhotoSchema>;
|
||||
export type ProtocolPhoto = typeof protocolPhotosTable.$inferSelect;
|
||||
|
||||
export type ProtocolAuditLog = typeof protocolAuditLogsTable.$inferSelect;
|
||||
|
||||
Reference in New Issue
Block a user