Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
644
src/lib/db/schema/relations.ts
Normal file
644
src/lib/db/schema/relations.ts
Normal file
@@ -0,0 +1,644 @@
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
// Ports
|
||||
import { ports } from './ports';
|
||||
|
||||
// Users
|
||||
import { userProfiles, roles, portRoleOverrides, userPortRoles } from './users';
|
||||
|
||||
// Clients
|
||||
import {
|
||||
clients,
|
||||
clientContacts,
|
||||
clientRelationships,
|
||||
clientNotes,
|
||||
clientTags,
|
||||
clientMergeLog,
|
||||
} from './clients';
|
||||
|
||||
// Interests
|
||||
import { interests, interestNotes, interestTags } from './interests';
|
||||
|
||||
// Berths
|
||||
import {
|
||||
berths,
|
||||
berthMapData,
|
||||
berthRecommendations,
|
||||
berthWaitingList,
|
||||
berthMaintenanceLog,
|
||||
berthTags,
|
||||
} from './berths';
|
||||
|
||||
// Documents
|
||||
import {
|
||||
files,
|
||||
documents,
|
||||
documentSigners,
|
||||
documentEvents,
|
||||
documentTemplates,
|
||||
formTemplates,
|
||||
formSubmissions,
|
||||
} from './documents';
|
||||
|
||||
// Financial
|
||||
import { expenses, invoices, invoiceLineItems, invoiceExpenses } from './financial';
|
||||
|
||||
// Email
|
||||
import { emailAccounts, emailThreads, emailMessages } from './email';
|
||||
|
||||
// Operations
|
||||
import {
|
||||
reminders,
|
||||
googleCalendarCache,
|
||||
googleCalendarTokens,
|
||||
notifications,
|
||||
scheduledReports,
|
||||
reportRecipients,
|
||||
generatedReports,
|
||||
} from './operations';
|
||||
|
||||
// System
|
||||
import {
|
||||
auditLogs,
|
||||
tags,
|
||||
webhooks,
|
||||
webhookDeliveries,
|
||||
systemSettings,
|
||||
savedViews,
|
||||
scratchpadNotes,
|
||||
userNotificationPreferences,
|
||||
currencyRates,
|
||||
customFieldDefinitions,
|
||||
customFieldValues,
|
||||
} from './system';
|
||||
|
||||
// ─── Ports ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const portsRelations = relations(ports, ({ many }) => ({
|
||||
userPortRoles: many(userPortRoles),
|
||||
portRoleOverrides: many(portRoleOverrides),
|
||||
clients: many(clients),
|
||||
interests: many(interests),
|
||||
berths: many(berths),
|
||||
documents: many(documents),
|
||||
documentTemplates: many(documentTemplates),
|
||||
formTemplates: many(formTemplates),
|
||||
expenses: many(expenses),
|
||||
invoices: many(invoices),
|
||||
emailAccounts: many(emailAccounts),
|
||||
emailThreads: many(emailThreads),
|
||||
reminders: many(reminders),
|
||||
notifications: many(notifications),
|
||||
scheduledReports: many(scheduledReports),
|
||||
auditLogs: many(auditLogs),
|
||||
tags: many(tags),
|
||||
files: many(files),
|
||||
webhooks: many(webhooks),
|
||||
systemSettings: many(systemSettings),
|
||||
savedViews: many(savedViews),
|
||||
userNotificationPreferences: many(userNotificationPreferences),
|
||||
customFieldDefinitions: many(customFieldDefinitions),
|
||||
berthMaintenanceLogs: many(berthMaintenanceLog),
|
||||
clientMergeLogs: many(clientMergeLog),
|
||||
clientRelationships: many(clientRelationships),
|
||||
}));
|
||||
|
||||
// ─── Users ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const userProfilesRelations = relations(userProfiles, ({ many }) => ({
|
||||
userPortRoles: many(userPortRoles),
|
||||
}));
|
||||
|
||||
export const rolesRelations = relations(roles, ({ many }) => ({
|
||||
userPortRoles: many(userPortRoles),
|
||||
portRoleOverrides: many(portRoleOverrides),
|
||||
}));
|
||||
|
||||
export const portRoleOverridesRelations = relations(portRoleOverrides, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [portRoleOverrides.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
role: one(roles, {
|
||||
fields: [portRoleOverrides.roleId],
|
||||
references: [roles.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const userPortRolesRelations = relations(userPortRoles, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [userPortRoles.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
role: one(roles, {
|
||||
fields: [userPortRoles.roleId],
|
||||
references: [roles.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Clients ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export const clientsRelations = relations(clients, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [clients.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
contacts: many(clientContacts),
|
||||
notes: many(clientNotes),
|
||||
tags: many(clientTags),
|
||||
interests: many(interests),
|
||||
relationships_a: many(clientRelationships, { relationName: 'client_a' }),
|
||||
relationships_b: many(clientRelationships, { relationName: 'client_b' }),
|
||||
mergeLogsAsSurvivor: many(clientMergeLog),
|
||||
documents: many(documents),
|
||||
emailThreads: many(emailThreads),
|
||||
reminders: many(reminders),
|
||||
files: many(files),
|
||||
waitingListEntries: many(berthWaitingList),
|
||||
scratchpadNotes: many(scratchpadNotes),
|
||||
formSubmissions: many(formSubmissions),
|
||||
}));
|
||||
|
||||
export const clientContactsRelations = relations(clientContacts, ({ one }) => ({
|
||||
client: one(clients, {
|
||||
fields: [clientContacts.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const clientRelationshipsRelations = relations(clientRelationships, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [clientRelationships.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
clientA: one(clients, {
|
||||
fields: [clientRelationships.clientAId],
|
||||
references: [clients.id],
|
||||
relationName: 'client_a',
|
||||
}),
|
||||
clientB: one(clients, {
|
||||
fields: [clientRelationships.clientBId],
|
||||
references: [clients.id],
|
||||
relationName: 'client_b',
|
||||
}),
|
||||
}));
|
||||
|
||||
export const clientNotesRelations = relations(clientNotes, ({ one }) => ({
|
||||
client: one(clients, {
|
||||
fields: [clientNotes.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const clientTagsRelations = relations(clientTags, ({ one }) => ({
|
||||
client: one(clients, {
|
||||
fields: [clientTags.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
tag: one(tags, {
|
||||
fields: [clientTags.tagId],
|
||||
references: [tags.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const clientMergeLogRelations = relations(clientMergeLog, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [clientMergeLog.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
survivingClient: one(clients, {
|
||||
fields: [clientMergeLog.survivingClientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Interests ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const interestsRelations = relations(interests, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [interests.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [interests.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
berth: one(berths, {
|
||||
fields: [interests.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
notes: many(interestNotes),
|
||||
tags: many(interestTags),
|
||||
documents: many(documents),
|
||||
reminders: many(reminders),
|
||||
berthRecommendations: many(berthRecommendations),
|
||||
formSubmissions: many(formSubmissions),
|
||||
}));
|
||||
|
||||
export const interestNotesRelations = relations(interestNotes, ({ one }) => ({
|
||||
interest: one(interests, {
|
||||
fields: [interestNotes.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const interestTagsRelations = relations(interestTags, ({ one }) => ({
|
||||
interest: one(interests, {
|
||||
fields: [interestTags.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
tag: one(tags, {
|
||||
fields: [interestTags.tagId],
|
||||
references: [tags.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Berths ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export const berthsRelations = relations(berths, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [berths.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
mapData: one(berthMapData),
|
||||
recommendations: many(berthRecommendations),
|
||||
waitingList: many(berthWaitingList),
|
||||
maintenanceLogs: many(berthMaintenanceLog),
|
||||
tags: many(berthTags),
|
||||
interests: many(interests),
|
||||
reminders: many(reminders),
|
||||
}));
|
||||
|
||||
export const berthMapDataRelations = relations(berthMapData, ({ one }) => ({
|
||||
berth: one(berths, {
|
||||
fields: [berthMapData.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const berthRecommendationsRelations = relations(berthRecommendations, ({ one }) => ({
|
||||
interest: one(interests, {
|
||||
fields: [berthRecommendations.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
berth: one(berths, {
|
||||
fields: [berthRecommendations.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const berthWaitingListRelations = relations(berthWaitingList, ({ one }) => ({
|
||||
berth: one(berths, {
|
||||
fields: [berthWaitingList.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [berthWaitingList.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const berthMaintenanceLogRelations = relations(berthMaintenanceLog, ({ one }) => ({
|
||||
berth: one(berths, {
|
||||
fields: [berthMaintenanceLog.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
port: one(ports, {
|
||||
fields: [berthMaintenanceLog.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const berthTagsRelations = relations(berthTags, ({ one }) => ({
|
||||
berth: one(berths, {
|
||||
fields: [berthTags.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
tag: one(tags, {
|
||||
fields: [berthTags.tagId],
|
||||
references: [tags.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Documents ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const filesRelations = relations(files, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [files.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [files.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
documentAsFile: many(documents, { relationName: 'file' }),
|
||||
documentAsSignedFile: many(documents, { relationName: 'signed_file' }),
|
||||
}));
|
||||
|
||||
export const documentsRelations = relations(documents, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [documents.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
interest: one(interests, {
|
||||
fields: [documents.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [documents.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
file: one(files, {
|
||||
fields: [documents.fileId],
|
||||
references: [files.id],
|
||||
relationName: 'file',
|
||||
}),
|
||||
signedFile: one(files, {
|
||||
fields: [documents.signedFileId],
|
||||
references: [files.id],
|
||||
relationName: 'signed_file',
|
||||
}),
|
||||
signers: many(documentSigners),
|
||||
events: many(documentEvents),
|
||||
}));
|
||||
|
||||
export const documentSignersRelations = relations(documentSigners, ({ one, many }) => ({
|
||||
document: one(documents, {
|
||||
fields: [documentSigners.documentId],
|
||||
references: [documents.id],
|
||||
}),
|
||||
events: many(documentEvents),
|
||||
}));
|
||||
|
||||
export const documentEventsRelations = relations(documentEvents, ({ one }) => ({
|
||||
document: one(documents, {
|
||||
fields: [documentEvents.documentId],
|
||||
references: [documents.id],
|
||||
}),
|
||||
signer: one(documentSigners, {
|
||||
fields: [documentEvents.signerId],
|
||||
references: [documentSigners.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const documentTemplatesRelations = relations(documentTemplates, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [documentTemplates.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const formTemplatesRelations = relations(formTemplates, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [formTemplates.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
submissions: many(formSubmissions),
|
||||
}));
|
||||
|
||||
export const formSubmissionsRelations = relations(formSubmissions, ({ one }) => ({
|
||||
formTemplate: one(formTemplates, {
|
||||
fields: [formSubmissions.formTemplateId],
|
||||
references: [formTemplates.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [formSubmissions.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
interest: one(interests, {
|
||||
fields: [formSubmissions.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Financial ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const expensesRelations = relations(expenses, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [expenses.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
invoiceExpenses: many(invoiceExpenses),
|
||||
}));
|
||||
|
||||
export const invoicesRelations = relations(invoices, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [invoices.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
pdfFile: one(files, {
|
||||
fields: [invoices.pdfFileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
lineItems: many(invoiceLineItems),
|
||||
invoiceExpenses: many(invoiceExpenses),
|
||||
}));
|
||||
|
||||
export const invoiceLineItemsRelations = relations(invoiceLineItems, ({ one }) => ({
|
||||
invoice: one(invoices, {
|
||||
fields: [invoiceLineItems.invoiceId],
|
||||
references: [invoices.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const invoiceExpensesRelations = relations(invoiceExpenses, ({ one }) => ({
|
||||
invoice: one(invoices, {
|
||||
fields: [invoiceExpenses.invoiceId],
|
||||
references: [invoices.id],
|
||||
}),
|
||||
expense: one(expenses, {
|
||||
fields: [invoiceExpenses.expenseId],
|
||||
references: [expenses.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Email ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const emailAccountsRelations = relations(emailAccounts, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [emailAccounts.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const emailThreadsRelations = relations(emailThreads, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [emailThreads.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [emailThreads.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
messages: many(emailMessages),
|
||||
}));
|
||||
|
||||
export const emailMessagesRelations = relations(emailMessages, ({ one }) => ({
|
||||
thread: one(emailThreads, {
|
||||
fields: [emailMessages.threadId],
|
||||
references: [emailThreads.id],
|
||||
}),
|
||||
rawFile: one(files, {
|
||||
fields: [emailMessages.rawFileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── Operations ───────────────────────────────────────────────────────────────
|
||||
|
||||
export const remindersRelations = relations(reminders, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [reminders.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
client: one(clients, {
|
||||
fields: [reminders.clientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
interest: one(interests, {
|
||||
fields: [reminders.interestId],
|
||||
references: [interests.id],
|
||||
}),
|
||||
berth: one(berths, {
|
||||
fields: [reminders.berthId],
|
||||
references: [berths.id],
|
||||
}),
|
||||
calendarCacheEntries: many(googleCalendarCache),
|
||||
}));
|
||||
|
||||
export const googleCalendarTokensRelations = relations(googleCalendarTokens, ({ many }) => ({
|
||||
cacheEntries: many(googleCalendarCache),
|
||||
}));
|
||||
|
||||
export const googleCalendarCacheRelations = relations(googleCalendarCache, ({ one }) => ({
|
||||
reminder: one(reminders, {
|
||||
fields: [googleCalendarCache.reminderId],
|
||||
references: [reminders.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [notifications.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const scheduledReportsRelations = relations(scheduledReports, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [scheduledReports.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
recipients: many(reportRecipients),
|
||||
generatedReports: many(generatedReports),
|
||||
}));
|
||||
|
||||
export const reportRecipientsRelations = relations(reportRecipients, ({ one }) => ({
|
||||
report: one(scheduledReports, {
|
||||
fields: [reportRecipients.reportId],
|
||||
references: [scheduledReports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const generatedReportsRelations = relations(generatedReports, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [generatedReports.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
scheduledReport: one(scheduledReports, {
|
||||
fields: [generatedReports.scheduledReportId],
|
||||
references: [scheduledReports.id],
|
||||
}),
|
||||
file: one(files, {
|
||||
fields: [generatedReports.fileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
// ─── System ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export const auditLogsRelations = relations(auditLogs, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [auditLogs.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
revertOfLog: one(auditLogs, {
|
||||
fields: [auditLogs.revertOf],
|
||||
references: [auditLogs.id],
|
||||
relationName: 'revert_of',
|
||||
}),
|
||||
}));
|
||||
|
||||
export const tagsRelations = relations(tags, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [tags.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
clientTags: many(clientTags),
|
||||
interestTags: many(interestTags),
|
||||
berthTags: many(berthTags),
|
||||
}));
|
||||
|
||||
export const webhooksRelations = relations(webhooks, ({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [webhooks.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
deliveries: many(webhookDeliveries),
|
||||
}));
|
||||
|
||||
export const webhookDeliveriesRelations = relations(webhookDeliveries, ({ one }) => ({
|
||||
webhook: one(webhooks, {
|
||||
fields: [webhookDeliveries.webhookId],
|
||||
references: [webhooks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const systemSettingsRelations = relations(systemSettings, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [systemSettings.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const savedViewsRelations = relations(savedViews, ({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [savedViews.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const scratchpadNotesRelations = relations(scratchpadNotes, ({ one }) => ({
|
||||
linkedClient: one(clients, {
|
||||
fields: [scratchpadNotes.linkedClientId],
|
||||
references: [clients.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const userNotificationPreferencesRelations = relations(
|
||||
userNotificationPreferences,
|
||||
({ one }) => ({
|
||||
port: one(ports, {
|
||||
fields: [userNotificationPreferences.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
export const customFieldDefinitionsRelations = relations(
|
||||
customFieldDefinitions,
|
||||
({ one, many }) => ({
|
||||
port: one(ports, {
|
||||
fields: [customFieldDefinitions.portId],
|
||||
references: [ports.id],
|
||||
}),
|
||||
values: many(customFieldValues),
|
||||
}),
|
||||
);
|
||||
|
||||
export const customFieldValuesRelations = relations(customFieldValues, ({ one }) => ({
|
||||
definition: one(customFieldDefinitions, {
|
||||
fields: [customFieldValues.fieldId],
|
||||
references: [customFieldDefinitions.id],
|
||||
}),
|
||||
}));
|
||||
Reference in New Issue
Block a user