Initial commit: Port Nimara CRM (Layers 0-4)
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled

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:
2026-03-26 11:52:51 +01:00
commit 67d7e6e3d5
572 changed files with 86496 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
import { z } from 'zod';
import { baseListQuerySchema } from '@/lib/api/route-helpers';
export const createTemplateSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().max(500).optional(),
templateType: z.enum([
'welcome_letter',
'handover_checklist',
'acknowledgment',
'correspondence',
'custom',
]),
bodyHtml: z.string().min(1),
mergeFields: z.array(z.string()).optional().default([]),
isActive: z.boolean().default(true),
});
export const updateTemplateSchema = createTemplateSchema.partial();
export const listTemplatesSchema = baseListQuerySchema.extend({
templateType: z.string().optional(),
isActive: z
.enum(['true', 'false'])
.transform((v) => v === 'true')
.optional(),
});
export const generateSchema = z.object({
clientId: z.string().optional(),
interestId: z.string().optional(),
berthId: z.string().optional(),
});
export const generateAndSendSchema = generateSchema.extend({
recipientEmail: z.string().email(),
});
export const generateAndSignSchema = generateSchema.extend({
signers: z
.array(
z.object({
name: z.string().min(1),
email: z.string().email(),
role: z.string().min(1),
signingOrder: z.number().int().min(1),
}),
)
.min(1),
});
export type CreateTemplateInput = z.infer<typeof createTemplateSchema>;
export type UpdateTemplateInput = z.infer<typeof updateTemplateSchema>;
export type ListTemplatesInput = z.infer<typeof listTemplatesSchema>;
export type GenerateInput = z.infer<typeof generateSchema>;
export type GenerateAndSendInput = z.infer<typeof generateAndSendSchema>;
export type GenerateAndSignInput = z.infer<typeof generateAndSignSchema>;
// ─── TipTap-based Admin Template Schemas ─────────────────────────────────────
// Used by /api/v1/admin/templates — the TipTap JSON document store.
export const tiptapDocumentTypes = [
'eoi',
'contract',
'nda',
'reservation_agreement',
'letter',
'other',
] as const;
export const createAdminTemplateSchema = z.object({
name: z.string().min(1).max(200),
type: z.enum(tiptapDocumentTypes),
content: z.record(z.unknown()), // TipTap JSON document
});
export const updateAdminTemplateSchema = z.object({
name: z.string().min(1).max(200).optional(),
content: z.record(z.unknown()).optional(),
isActive: z.boolean().optional(),
});
export const previewAdminTemplateSchema = z.object({
content: z.record(z.unknown()),
sampleData: z.record(z.string()).optional(),
});
export const rollbackAdminTemplateSchema = z.object({
version: z.number().int().min(1),
});
export const listAdminTemplatesSchema = baseListQuerySchema.extend({
type: z.enum(tiptapDocumentTypes).optional(),
isActive: z
.enum(['true', 'false'])
.transform((v) => v === 'true')
.optional(),
});
export type CreateAdminTemplateInput = z.infer<typeof createAdminTemplateSchema>;
export type UpdateAdminTemplateInput = z.infer<typeof updateAdminTemplateSchema>;
export type PreviewAdminTemplateInput = z.infer<typeof previewAdminTemplateSchema>;
export type RollbackAdminTemplateInput = z.infer<typeof rollbackAdminTemplateSchema>;
export type ListAdminTemplatesInput = z.infer<typeof listAdminTemplatesSchema>;