import { createId } from "@paralleldrive/cuid2"; import { relations } from "drizzle-orm"; import { boolean, index, integer, pgEnum, pgTable, text, timestamp, uniqueIndex, } from "drizzle-orm/pg-core"; import { MediaKind, MediaSource, MediaUsageType, PortfolioAssetKind, PortfolioProjectViewMode, PortfolioSectionType, enumValues, } from "./enums"; // Postgres enum types — names match the ones Prisma created, so no DB migration // is needed for the ORM swap. export const portfolioSectionTypeEnum = pgEnum("PortfolioSectionType", enumValues(PortfolioSectionType)); export const portfolioAssetKindEnum = pgEnum("PortfolioAssetKind", enumValues(PortfolioAssetKind)); export const portfolioProjectViewModeEnum = pgEnum("PortfolioProjectViewMode", enumValues(PortfolioProjectViewMode)); export const mediaSourceEnum = pgEnum("MediaSource", enumValues(MediaSource)); export const mediaKindEnum = pgEnum("MediaKind", enumValues(MediaKind)); export const mediaUsageTypeEnum = pgEnum("MediaUsageType", enumValues(MediaUsageType)); // Shared column builders (Prisma parity): cuid ids, precision-3 timestamps. const id = () => text("id").primaryKey().$defaultFn(() => createId()); const createdAt = () => timestamp("createdAt", { precision: 3, mode: "date" }).defaultNow().notNull(); const updatedAt = () => timestamp("updatedAt", { precision: 3, mode: "date" }) .defaultNow() .notNull() .$onUpdate(() => new Date()); export const appConfig = pgTable("AppConfig", { id: id(), key: text("key").notNull().unique(), value: text("value").notNull(), createdAt: createdAt(), updatedAt: updatedAt(), }); export const category = pgTable( "Category", { id: id(), slug: text("slug").notNull().unique(), nameAr: text("nameAr").notNull(), nameEn: text("nameEn").notNull(), nameDe: text("nameDe").notNull(), descriptionAr: text("descriptionAr").notNull(), descriptionEn: text("descriptionEn").notNull(), descriptionDe: text("descriptionDe").notNull(), sortOrder: integer("sortOrder").notNull().default(0), isActive: boolean("isActive").notNull().default(true), createdAt: createdAt(), updatedAt: updatedAt(), }, ); export const portfolioProject = pgTable( "PortfolioProject", { id: id(), categoryId: text("categoryId") .notNull() .references(() => category.id, { onDelete: "restrict" }), slug: text("slug").notNull().unique(), viewMode: portfolioProjectViewModeEnum("viewMode").notNull().default("GRID"), titleAr: text("titleAr").notNull(), titleEn: text("titleEn").notNull(), titleDe: text("titleDe").notNull(), summaryAr: text("summaryAr").notNull(), summaryEn: text("summaryEn").notNull(), summaryDe: text("summaryDe").notNull(), clientName: text("clientName").notNull(), projectYear: integer("projectYear").notNull(), serviceLabelAr: text("serviceLabelAr").notNull(), serviceLabelEn: text("serviceLabelEn").notNull(), serviceLabelDe: text("serviceLabelDe").notNull(), previewUrl: text("previewUrl"), coverImagePath: text("coverImagePath"), isFeatured: boolean("isFeatured").notNull().default(false), isPublished: boolean("isPublished").notNull().default(false), publishedAt: timestamp("publishedAt", { precision: 3, mode: "date" }), sortOrder: integer("sortOrder").notNull().default(0), createdAt: createdAt(), updatedAt: updatedAt(), }, (table) => [ index("PortfolioProject_categoryId_isPublished_sortOrder_idx").on( table.categoryId, table.isPublished, table.sortOrder, ), index("PortfolioProject_isPublished_sortOrder_idx").on(table.isPublished, table.sortOrder), ], ); export const portfolioSection = pgTable( "PortfolioSection", { id: id(), projectId: text("projectId") .notNull() .references(() => portfolioProject.id, { onDelete: "cascade" }), type: portfolioSectionTypeEnum("type").notNull(), titleAr: text("titleAr").notNull(), titleEn: text("titleEn").notNull(), titleDe: text("titleDe").notNull(), bodyAr: text("bodyAr").notNull(), bodyEn: text("bodyEn").notNull(), bodyDe: text("bodyDe").notNull(), imagePath: text("imagePath"), linkUrl: text("linkUrl"), sortOrder: integer("sortOrder").notNull().default(0), createdAt: createdAt(), updatedAt: updatedAt(), }, (table) => [index("PortfolioSection_projectId_sortOrder_idx").on(table.projectId, table.sortOrder)], ); export const portfolioAsset = pgTable( "PortfolioAsset", { id: id(), projectId: text("projectId") .notNull() .references(() => portfolioProject.id, { onDelete: "cascade" }), kind: portfolioAssetKindEnum("kind").notNull(), filePath: text("filePath").notNull(), altAr: text("altAr").notNull(), altEn: text("altEn").notNull(), altDe: text("altDe").notNull(), sortOrder: integer("sortOrder").notNull().default(0), createdAt: createdAt(), updatedAt: updatedAt(), }, (table) => [index("PortfolioAsset_projectId_sortOrder_idx").on(table.projectId, table.sortOrder)], ); export const mediaAsset = pgTable( "MediaAsset", { id: id(), source: mediaSourceEnum("source").notNull(), kind: mediaKindEnum("kind").notNull(), url: text("url").notNull(), fileName: text("fileName").notNull(), label: text("label").notNull(), altText: text("altText"), mimeType: text("mimeType"), size: integer("size"), createdAt: createdAt(), updatedAt: updatedAt(), }, (table) => [index("MediaAsset_kind_createdAt_idx").on(table.kind, table.createdAt)], ); export const mediaUsage = pgTable( "MediaUsage", { id: id(), assetId: text("assetId") .notNull() .references(() => mediaAsset.id, { onDelete: "cascade" }), usageType: mediaUsageTypeEnum("usageType").notNull(), entityType: text("entityType").notNull(), entityId: text("entityId").notNull(), fieldKey: text("fieldKey").notNull(), createdAt: createdAt(), updatedAt: updatedAt(), }, (table) => [ uniqueIndex("MediaUsage_usageType_entityType_entityId_fieldKey_key").on( table.usageType, table.entityType, table.entityId, table.fieldKey, ), index("MediaUsage_assetId_idx").on(table.assetId), index("MediaUsage_entityType_entityId_idx").on(table.entityType, table.entityId), ], ); // Relations (enable db.query.* `with:` includes). export const categoryRelations = relations(category, ({ many }) => ({ projects: many(portfolioProject), })); export const portfolioProjectRelations = relations(portfolioProject, ({ one, many }) => ({ category: one(category, { fields: [portfolioProject.categoryId], references: [category.id], }), sections: many(portfolioSection), assets: many(portfolioAsset), })); export const portfolioSectionRelations = relations(portfolioSection, ({ one }) => ({ project: one(portfolioProject, { fields: [portfolioSection.projectId], references: [portfolioProject.id], }), })); export const portfolioAssetRelations = relations(portfolioAsset, ({ one }) => ({ project: one(portfolioProject, { fields: [portfolioAsset.projectId], references: [portfolioProject.id], }), })); export const mediaAssetRelations = relations(mediaAsset, ({ many }) => ({ usages: many(mediaUsage), })); export const mediaUsageRelations = relations(mediaUsage, ({ one }) => ({ asset: one(mediaAsset, { fields: [mediaUsage.assetId], references: [mediaAsset.id], }), })); // Inferred row types (replace the old `@prisma/client` model type imports). export type AppConfig = typeof appConfig.$inferSelect; export type Category = typeof category.$inferSelect; export type PortfolioProject = typeof portfolioProject.$inferSelect; export type PortfolioSection = typeof portfolioSection.$inferSelect; export type PortfolioAsset = typeof portfolioAsset.$inferSelect; export type MediaAsset = typeof mediaAsset.$inferSelect; export type MediaUsage = typeof mediaUsage.$inferSelect; export { MediaKind, MediaSource, MediaUsageType, PortfolioAssetKind, PortfolioProjectViewMode, PortfolioSectionType, } from "./enums";