feat: dateformat and timezone customization (#506)

This commit is contained in:
hallidayo
2023-12-26 23:50:40 +00:00
committed by Mythie
parent 27b7e29be7
commit 32633f96d2
40 changed files with 517 additions and 94 deletions

View File

@ -1,4 +1,5 @@
import { ReadStatus, Recipient, SendStatus, SigningStatus } from '@documenso/prisma/client';
import type { Recipient } from '@documenso/prisma/client';
import { ReadStatus, SendStatus, SigningStatus } from '@documenso/prisma/client';
export const getRecipientType = (recipient: Recipient) => {
if (

View File

@ -0,0 +1,71 @@
import { DateTime } from 'luxon';
import { DEFAULT_DOCUMENT_TIME_ZONE } from './time-zones';
export const DEFAULT_DOCUMENT_DATE_FORMAT = 'yyyy-MM-dd hh:mm a';
export const DATE_FORMATS = [
{
key: 'YYYYMMDD',
label: 'YYYY-MM-DD',
value: DEFAULT_DOCUMENT_DATE_FORMAT,
},
{
key: 'DDMMYYYY',
label: 'DD/MM/YYYY',
value: 'dd/MM/yyyy hh:mm a',
},
{
key: 'MMDDYYYY',
label: 'MM/DD/YYYY',
value: 'MM/dd/yyyy hh:mm a',
},
{
key: 'YYYYMMDDHHmm',
label: 'YYYY-MM-DD HH:mm',
value: 'yyyy-MM-dd HH:mm',
},
{
key: 'YYMMDD',
label: 'YY-MM-DD',
value: 'yy-MM-dd hh:mm a',
},
{
key: 'YYYYMMDDhhmmss',
label: 'YYYY-MM-DD HH:mm:ss',
value: 'yyyy-MM-dd HH:mm:ss',
},
{
key: 'MonthDateYear',
label: 'Month Date, Year',
value: 'MMMM dd, yyyy hh:mm a',
},
{
key: 'DayMonthYear',
label: 'Day, Month Year',
value: 'EEEE, MMMM dd, yyyy hh:mm a',
},
{
key: 'ISO8601',
label: 'ISO 8601',
value: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
},
];
export const convertToLocalSystemFormat = (
customText: string,
dateFormat: string | null = DEFAULT_DOCUMENT_DATE_FORMAT,
timeZone: string | null = DEFAULT_DOCUMENT_TIME_ZONE,
): string => {
const parsedDate = DateTime.fromFormat(customText, dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT, {
zone: timeZone ?? DEFAULT_DOCUMENT_TIME_ZONE,
});
if (!parsedDate.isValid) {
return 'Invalid date';
}
const formattedDate = parsedDate.toLocal().toFormat(dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT);
return formattedDate;
};

View File

@ -0,0 +1,44 @@
import { rawTimeZones, timeZonesNames } from '@vvo/tzdb';
export const TIME_ZONE_DATA = rawTimeZones;
export const DEFAULT_DOCUMENT_TIME_ZONE = 'Etc/UTC';
export type TimeZone = {
name: string;
rawOffsetInMinutes: number;
};
export const minutesToHours = (minutes: number): string => {
const hours = Math.abs(Math.floor(minutes / 60));
const min = Math.abs(minutes % 60);
const sign = minutes >= 0 ? '+' : '-';
return `${sign}${String(hours).padStart(2, '0')}:${String(min).padStart(2, '0')}`;
};
const getGMTOffsets = (timezones: TimeZone[]): string[] => {
const gmtOffsets: string[] = [];
for (const timezone of timezones) {
const offsetValue = minutesToHours(timezone.rawOffsetInMinutes);
const gmtText = `(${offsetValue})`;
gmtOffsets.push(`${timezone.name} ${gmtText}`);
}
return gmtOffsets;
};
export const splitTimeZone = (input: string | null): string => {
if (input === null) {
return '';
}
const [timeZone] = input.split('(');
return timeZone.trim();
};
export const TIME_ZONES_FULL = getGMTOffsets(TIME_ZONE_DATA);
export const TIME_ZONES = ['Etc/UTC', ...timeZonesNames];

View File

@ -31,6 +31,7 @@
"@scure/base": "^1.1.3",
"@sindresorhus/slugify": "^2.2.1",
"@upstash/redis": "^1.20.6",
"@vvo/tzdb": "^6.117.0",
"bcrypt": "^5.1.0",
"luxon": "^3.4.0",
"nanoid": "^4.0.2",

View File

@ -6,11 +6,15 @@ export type CreateDocumentMetaOptions = {
documentId: number;
subject: string;
message: string;
timezone: string;
dateFormat: string;
};
export const upsertDocumentMeta = async ({
subject,
message,
timezone,
dateFormat,
documentId,
}: CreateDocumentMetaOptions) => {
return await prisma.documentMeta.upsert({
@ -20,11 +24,15 @@ export const upsertDocumentMeta = async ({
create: {
subject,
message,
dateFormat,
timezone,
documentId,
},
update: {
subject,
message,
dateFormat,
timezone,
},
});
};

View File

@ -25,6 +25,8 @@ export const duplicateDocumentById = async ({ id, userId }: DuplicateDocumentByI
select: {
message: true,
subject: true,
dateFormat: true,
timezone: true,
},
},
},

View File

@ -0,0 +1,13 @@
import { prisma } from '@documenso/prisma';
export interface GetDocumentMetaByDocumentIdOptions {
id: number;
}
export const getDocumentMetaByDocumentId = async ({ id }: GetDocumentMetaByDocumentIdOptions) => {
return await prisma.documentMeta.findFirstOrThrow({
where: {
documentId: id,
},
});
};

View File

@ -1,6 +1,6 @@
'use server';
import { Prisma } from '@prisma/client';
import type { Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';

View File

@ -5,6 +5,9 @@ import { DateTime } from 'luxon';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, FieldType, SigningStatus } from '@documenso/prisma/client';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../../constants/date-formats';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '../../constants/time-zones';
export type SignFieldWithTokenOptions = {
token: string;
fieldId: number;
@ -58,6 +61,12 @@ export const signFieldWithToken = async ({
throw new Error(`Field ${fieldId} has no recipientId`);
}
const documentMeta = await prisma.documentMeta.findFirst({
where: {
documentId: document.id,
},
});
const isSignatureField =
field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE;
@ -67,7 +76,9 @@ export const signFieldWithToken = async ({
const typedSignature = isSignatureField && !isBase64 ? value : undefined;
if (field.type === FieldType.DATE) {
customText = DateTime.now().toFormat('yyyy-MM-dd hh:mm a');
customText = DateTime.now()
.setZone(documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE)
.toFormat(documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT);
}
if (isSignatureField && !signatureImageAsBase64 && !typedSignature) {

View File

@ -1,4 +1,4 @@
import { Recipient } from '@documenso/prisma/client';
import type { Recipient } from '@documenso/prisma/client';
export const recipientInitials = (text: string) =>
text