mirror of
https://github.com/documenso/documenso.git
synced 2026-07-15 15:27:10 +10:00
408 lines
14 KiB
TypeScript
408 lines
14 KiB
TypeScript
import type { ComponentType } from 'react';
|
|
|
|
import { AccessAuth2FAEmailTemplate } from '../../../templates/access-auth-2fa';
|
|
import { AdminUserCreatedTemplate } from '../../../templates/admin-user-created';
|
|
import { BulkSendCompleteEmail } from '../../../templates/bulk-send-complete';
|
|
import { ConfirmEmailTemplate } from '../../../templates/confirm-email';
|
|
import { ConfirmTeamEmailTemplate } from '../../../templates/confirm-team-email';
|
|
import { DocumentCancelTemplate } from '../../../templates/document-cancel';
|
|
import { DocumentCompletedEmailTemplate } from '../../../templates/document-completed';
|
|
import { DocumentCreatedFromDirectTemplateEmailTemplate } from '../../../templates/document-created-from-direct-template';
|
|
import { DocumentInviteEmailTemplate } from '../../../templates/document-invite';
|
|
import { DocumentPendingEmailTemplate } from '../../../templates/document-pending';
|
|
import { DocumentRecipientSignedEmailTemplate } from '../../../templates/document-recipient-signed';
|
|
import { DocumentRejectedEmail } from '../../../templates/document-rejected';
|
|
import { DocumentRejectionConfirmedEmail } from '../../../templates/document-rejection-confirmed';
|
|
import { DocumentReminderEmailTemplate } from '../../../templates/document-reminder';
|
|
import { DocumentSelfSignedEmailTemplate } from '../../../templates/document-self-signed';
|
|
import { DocumentSuperDeleteEmailTemplate } from '../../../templates/document-super-delete';
|
|
import { ForgotPasswordTemplate } from '../../../templates/forgot-password';
|
|
import { OrganisationAccountLinkConfirmationTemplate } from '../../../templates/organisation-account-link-confirmation';
|
|
import { OrganisationDeleteEmailTemplate } from '../../../templates/organisation-delete';
|
|
import { OrganisationInviteEmailTemplate } from '../../../templates/organisation-invite';
|
|
import { OrganisationJoinEmailTemplate } from '../../../templates/organisation-join';
|
|
import { OrganisationLeaveEmailTemplate } from '../../../templates/organisation-leave';
|
|
import { OrganisationLimitAlertEmailTemplate } from '../../../templates/organisation-limit-alert';
|
|
import { RecipientExpiredTemplate } from '../../../templates/recipient-expired';
|
|
import { RecipientRemovedFromDocumentTemplate } from '../../../templates/recipient-removed-from-document';
|
|
import { ResetPasswordTemplate } from '../../../templates/reset-password';
|
|
import { TeamDeleteEmailTemplate } from '../../../templates/team-delete';
|
|
import { TeamEmailRemovedTemplate } from '../../../templates/team-email-removed';
|
|
|
|
export type FieldType = 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'list';
|
|
|
|
export type FieldConfig = {
|
|
type: FieldType;
|
|
label: string;
|
|
description?: string;
|
|
placeholder?: string;
|
|
default: unknown;
|
|
options?: { label: string; value: string }[];
|
|
};
|
|
|
|
export type TemplateDefinition = {
|
|
/** Human label for the sidebar. */
|
|
name: string;
|
|
/** Loose grouping for the sidebar. */
|
|
group: 'Documents' | 'Recipients' | 'Organisations' | 'Teams' | 'Account' | 'Admin';
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
component: ComponentType<any>;
|
|
/** Editable props surfaced in the preview UI. */
|
|
fields: Record<string, FieldConfig>;
|
|
};
|
|
|
|
// --- Reusable field presets ---
|
|
|
|
const documentNameField: FieldConfig = {
|
|
type: 'text',
|
|
label: 'Document name',
|
|
default: 'Open Source Pledge.pdf',
|
|
};
|
|
|
|
const recipientNameField: FieldConfig = {
|
|
type: 'text',
|
|
label: 'Recipient name',
|
|
default: 'Lucas Smith',
|
|
};
|
|
|
|
const roleField: FieldConfig = {
|
|
type: 'select',
|
|
label: 'Recipient role',
|
|
default: 'SIGNER',
|
|
options: [
|
|
{ label: 'Signer', value: 'SIGNER' },
|
|
{ label: 'Viewer', value: 'VIEWER' },
|
|
{ label: 'Approver', value: 'APPROVER' },
|
|
{ label: 'CC', value: 'CC' },
|
|
{ label: 'Assistant', value: 'ASSISTANT' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* Explicit template registry. Each entry maps a slug → component + editable
|
|
* `fields`. The slug is the route param (`/:slug`) and matches the source
|
|
* filename (sans extension).
|
|
*
|
|
* `fields` drives both the default preview values AND the editable inputs in
|
|
* the UI, so production templates stay free of preview-only defaults.
|
|
*/
|
|
export const templates: Record<string, TemplateDefinition> = {
|
|
// ---- Documents ----
|
|
'document-invite': {
|
|
name: 'Document invite',
|
|
group: 'Documents',
|
|
component: DocumentInviteEmailTemplate,
|
|
fields: {
|
|
inviterName: { type: 'text', label: 'Inviter name', default: 'Lucas Smith' },
|
|
inviterEmail: { type: 'text', label: 'Inviter email', default: 'lucas@documenso.com' },
|
|
documentName: documentNameField,
|
|
role: roleField,
|
|
customBody: {
|
|
type: 'textarea',
|
|
label: 'Custom message',
|
|
default: '',
|
|
description: 'Leave blank to use the default invite copy.',
|
|
},
|
|
},
|
|
},
|
|
'document-completed': {
|
|
name: 'Document completed',
|
|
group: 'Documents',
|
|
component: DocumentCompletedEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
customBody: { type: 'textarea', label: 'Custom message', default: '' },
|
|
},
|
|
},
|
|
'document-self-signed': {
|
|
name: 'Document self-signed',
|
|
group: 'Documents',
|
|
component: DocumentSelfSignedEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
},
|
|
},
|
|
'document-pending': {
|
|
name: 'Document pending',
|
|
group: 'Documents',
|
|
component: DocumentPendingEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
},
|
|
},
|
|
'document-reminder': {
|
|
name: 'Document reminder',
|
|
group: 'Documents',
|
|
component: DocumentReminderEmailTemplate,
|
|
fields: {
|
|
recipientName: recipientNameField,
|
|
documentName: documentNameField,
|
|
role: roleField,
|
|
customBody: { type: 'textarea', label: 'Custom message', default: '' },
|
|
},
|
|
},
|
|
'document-cancel': {
|
|
name: 'Document cancelled',
|
|
group: 'Documents',
|
|
component: DocumentCancelTemplate,
|
|
fields: {
|
|
inviterName: { type: 'text', label: 'Inviter name', default: 'Lucas Smith' },
|
|
documentName: documentNameField,
|
|
cancellationReason: {
|
|
type: 'textarea',
|
|
label: 'Cancellation reason',
|
|
default: '',
|
|
description: 'Optional. Blank renders no reason block.',
|
|
},
|
|
},
|
|
},
|
|
'document-rejected': {
|
|
name: 'Document rejected',
|
|
group: 'Documents',
|
|
component: DocumentRejectedEmail,
|
|
fields: {
|
|
recipientName: recipientNameField,
|
|
documentName: documentNameField,
|
|
documentUrl: { type: 'text', label: 'Document URL', default: 'https://documenso.com' },
|
|
rejectionReason: {
|
|
type: 'textarea',
|
|
label: 'Rejection reason',
|
|
default: 'The pledge amount is incorrect.',
|
|
description: 'Optional in production; blank renders no reason block.',
|
|
},
|
|
},
|
|
},
|
|
'document-rejection-confirmed': {
|
|
name: 'Document rejection confirmed',
|
|
group: 'Documents',
|
|
component: DocumentRejectionConfirmedEmail,
|
|
fields: {
|
|
recipientName: recipientNameField,
|
|
documentName: documentNameField,
|
|
documentOwnerName: { type: 'text', label: 'Document owner', default: 'Timur Ercan' },
|
|
reason: {
|
|
type: 'textarea',
|
|
label: 'Rejection reason',
|
|
default: 'The pledge amount is incorrect.',
|
|
description: 'Optional in production; blank renders no reason block.',
|
|
},
|
|
},
|
|
},
|
|
'document-created-from-direct-template': {
|
|
name: 'Document created (direct template)',
|
|
group: 'Documents',
|
|
component: DocumentCreatedFromDirectTemplateEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
},
|
|
},
|
|
'document-super-delete': {
|
|
name: 'Document deleted (admin)',
|
|
group: 'Documents',
|
|
component: DocumentSuperDeleteEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
},
|
|
},
|
|
'bulk-send-complete': {
|
|
name: 'Bulk send complete',
|
|
group: 'Documents',
|
|
component: BulkSendCompleteEmail,
|
|
fields: {
|
|
userName: { type: 'text', label: 'User name', default: 'Lucas Smith' },
|
|
templateName: { type: 'text', label: 'Template name', default: 'NDA Template' },
|
|
totalProcessed: { type: 'number', label: 'Total processed', default: 50 },
|
|
successCount: { type: 'number', label: 'Success count', default: 48 },
|
|
failedCount: { type: 'number', label: 'Failed count', default: 2 },
|
|
errors: {
|
|
type: 'list',
|
|
label: 'Errors',
|
|
default: ['Row 12: invalid email', 'Row 30: missing name'],
|
|
description: 'One error per line. Rendered when failed count > 0.',
|
|
},
|
|
},
|
|
},
|
|
|
|
// ---- Recipients ----
|
|
'document-recipient-signed': {
|
|
name: 'Recipient signed',
|
|
group: 'Recipients',
|
|
component: DocumentRecipientSignedEmailTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
recipientName: recipientNameField,
|
|
},
|
|
},
|
|
'recipient-expired': {
|
|
name: 'Recipient expired',
|
|
group: 'Recipients',
|
|
component: RecipientExpiredTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
recipientName: recipientNameField,
|
|
},
|
|
},
|
|
'recipient-removed-from-document': {
|
|
name: 'Recipient removed',
|
|
group: 'Recipients',
|
|
component: RecipientRemovedFromDocumentTemplate,
|
|
fields: {
|
|
documentName: documentNameField,
|
|
},
|
|
},
|
|
|
|
// ---- Organisations ----
|
|
'organisation-invite': {
|
|
name: 'Organisation invite',
|
|
group: 'Organisations',
|
|
component: OrganisationInviteEmailTemplate,
|
|
fields: {
|
|
senderName: { type: 'text', label: 'Sender name', default: 'Lucas Smith' },
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'organisation-join': {
|
|
name: 'Organisation join',
|
|
group: 'Organisations',
|
|
component: OrganisationJoinEmailTemplate,
|
|
fields: {
|
|
memberName: { type: 'text', label: 'Member name', default: 'Lucas Smith' },
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'organisation-leave': {
|
|
name: 'Organisation leave',
|
|
group: 'Organisations',
|
|
component: OrganisationLeaveEmailTemplate,
|
|
fields: {
|
|
memberName: { type: 'text', label: 'Member name', default: 'Lucas Smith' },
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'organisation-delete': {
|
|
name: 'Organisation delete',
|
|
group: 'Organisations',
|
|
component: OrganisationDeleteEmailTemplate,
|
|
fields: {
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'organisation-limit-alert': {
|
|
name: 'Organisation limit alert',
|
|
group: 'Organisations',
|
|
component: OrganisationLimitAlertEmailTemplate,
|
|
fields: {
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'organisation-account-link-confirmation': {
|
|
name: 'Account link confirmation',
|
|
group: 'Organisations',
|
|
component: OrganisationAccountLinkConfirmationTemplate,
|
|
fields: {
|
|
organisationName: { type: 'text', label: 'Organisation name', default: 'Documenso' },
|
|
},
|
|
},
|
|
|
|
// ---- Teams ----
|
|
'confirm-team-email': {
|
|
name: 'Confirm team email',
|
|
group: 'Teams',
|
|
component: ConfirmTeamEmailTemplate,
|
|
fields: {
|
|
teamName: { type: 'text', label: 'Team name', default: 'Documenso' },
|
|
},
|
|
},
|
|
'team-delete': {
|
|
name: 'Team delete',
|
|
group: 'Teams',
|
|
component: TeamDeleteEmailTemplate,
|
|
fields: {},
|
|
},
|
|
'team-email-removed': {
|
|
name: 'Team email removed',
|
|
group: 'Teams',
|
|
component: TeamEmailRemovedTemplate,
|
|
fields: {
|
|
teamName: { type: 'text', label: 'Team name', default: 'Documenso' },
|
|
teamEmail: { type: 'text', label: 'Team email', default: 'team@documenso.com' },
|
|
},
|
|
},
|
|
|
|
// ---- Account ----
|
|
'confirm-email': {
|
|
name: 'Confirm email',
|
|
group: 'Account',
|
|
component: ConfirmEmailTemplate,
|
|
fields: {
|
|
confirmationLink: {
|
|
type: 'text',
|
|
label: 'Confirmation link',
|
|
default: 'https://documenso.com/confirm',
|
|
},
|
|
},
|
|
},
|
|
'forgot-password': {
|
|
name: 'Forgot password',
|
|
group: 'Account',
|
|
component: ForgotPasswordTemplate,
|
|
fields: {
|
|
resetPasswordLink: {
|
|
type: 'text',
|
|
label: 'Reset link',
|
|
default: 'https://documenso.com/reset',
|
|
},
|
|
},
|
|
},
|
|
'reset-password': {
|
|
name: 'Reset password',
|
|
group: 'Account',
|
|
component: ResetPasswordTemplate,
|
|
fields: {
|
|
userName: { type: 'text', label: 'User name', default: 'Lucas Smith' },
|
|
userEmail: { type: 'text', label: 'User email', default: 'lucas@documenso.com' },
|
|
},
|
|
},
|
|
'access-auth-2fa': {
|
|
name: 'Access auth 2FA',
|
|
group: 'Account',
|
|
component: AccessAuth2FAEmailTemplate,
|
|
fields: {
|
|
documentTitle: { type: 'text', label: 'Document title', default: 'Open Source Pledge.pdf' },
|
|
code: { type: 'text', label: 'Code', default: '123456' },
|
|
userEmail: { type: 'text', label: 'User email', default: 'lucas@documenso.com' },
|
|
userName: { type: 'text', label: 'User name', default: 'Lucas Smith' },
|
|
expiresInMinutes: { type: 'number', label: 'Expires in (min)', default: 10 },
|
|
},
|
|
},
|
|
|
|
// ---- Admin ----
|
|
'admin-user-created': {
|
|
name: 'Admin user created',
|
|
group: 'Admin',
|
|
component: AdminUserCreatedTemplate,
|
|
fields: {
|
|
resetPasswordLink: {
|
|
type: 'text',
|
|
label: 'Reset link',
|
|
default: 'https://documenso.com/reset',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export type TemplateId = keyof typeof templates;
|
|
|
|
/** Extract the default prop values from a template's field config. */
|
|
export const getDefaultProps = (fields: Record<string, FieldConfig>): Record<string, unknown> => {
|
|
const props: Record<string, unknown> = {};
|
|
|
|
for (const [key, field] of Object.entries(fields)) {
|
|
props[key] = field.default;
|
|
}
|
|
|
|
return props;
|
|
};
|
|
|
|
export const getTemplate = (slug: string): TemplateDefinition | undefined => templates[slug];
|