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>
2026-03-26 11:52:51 +01:00
|
|
|
import { z } from 'zod';
|
|
|
|
|
|
|
|
|
|
import { baseListQuerySchema } from '@/lib/api/route-helpers';
|
2026-04-26 13:48:06 +02:00
|
|
|
import { VALID_MERGE_TOKENS } from '@/lib/templates/merge-fields';
|
|
|
|
|
|
|
|
|
|
const mergeFieldsSchema = z
|
|
|
|
|
.array(z.string())
|
|
|
|
|
.optional()
|
|
|
|
|
.default([])
|
|
|
|
|
.refine(
|
|
|
|
|
(tokens) => tokens.every((t) => VALID_MERGE_TOKENS.has(t)),
|
|
|
|
|
(tokens) => {
|
|
|
|
|
const unknown = tokens?.filter((t) => !VALID_MERGE_TOKENS.has(t)) ?? [];
|
|
|
|
|
return { message: `Unknown merge tokens: ${unknown.join(', ')}` };
|
|
|
|
|
},
|
|
|
|
|
);
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
|
2026-04-28 02:12:05 +02:00
|
|
|
export const templateFormats = ['html', 'pdf_form', 'pdf_overlay', 'documenso_render'] as const;
|
|
|
|
|
|
|
|
|
|
const createTemplateBaseSchema = z.object({
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
name: z.string().min(1).max(200),
|
|
|
|
|
description: z.string().max(500).optional(),
|
|
|
|
|
templateType: z.enum([
|
2026-04-26 13:48:06 +02:00
|
|
|
'eoi',
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
'welcome_letter',
|
|
|
|
|
'handover_checklist',
|
|
|
|
|
'acknowledgment',
|
|
|
|
|
'correspondence',
|
|
|
|
|
'custom',
|
|
|
|
|
]),
|
2026-04-28 02:12:05 +02:00
|
|
|
templateFormat: z.enum(templateFormats).default('html'),
|
|
|
|
|
bodyHtml: z.string().min(1).optional(),
|
2026-04-26 13:48:06 +02:00
|
|
|
mergeFields: mergeFieldsSchema,
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
isActive: z.boolean().default(true),
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-28 02:12:05 +02:00
|
|
|
export const createTemplateSchema = createTemplateBaseSchema.refine(
|
|
|
|
|
(data) => data.templateFormat !== 'html' || (data.bodyHtml && data.bodyHtml.length > 0),
|
|
|
|
|
{ path: ['bodyHtml'], message: 'bodyHtml is required when templateFormat is html' },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const updateTemplateSchema = createTemplateBaseSchema.partial();
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
|
|
|
|
|
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({
|
2026-04-24 18:43:41 +02:00
|
|
|
pathway: z.enum(['inapp', 'documenso-template']).default('inapp'),
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
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),
|
|
|
|
|
}),
|
|
|
|
|
)
|
2026-04-24 18:43:41 +02:00
|
|
|
.optional()
|
|
|
|
|
.default([]),
|
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>
2026-03-26 11:52:51 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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>;
|