Date formatting per workspace member settings (#6408)
Implement date formatting per workspace member settings We'll need another round to maybe initialize all workspaces on the default settings. For now the default behavior is to take system settings if nothing is found in DB. --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { existsSync } from 'fs';
|
||||
import fs from 'fs/promises';
|
||||
import { join as joinPath } from 'path';
|
||||
|
||||
import { kebabCase } from 'src/utils/kebab-case';
|
||||
|
||||
@ -23,7 +24,7 @@ export class CommandLogger {
|
||||
fileName: string,
|
||||
data: unknown,
|
||||
append = false,
|
||||
): Promise<void> {
|
||||
): Promise<string> {
|
||||
const path = `./logs/${kebabCase(this.className)}`;
|
||||
|
||||
if (existsSync(path) === false) {
|
||||
@ -31,17 +32,20 @@ export class CommandLogger {
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.writeFile(
|
||||
`${path}/${fileName}.json`,
|
||||
JSON.stringify(data, null, 2),
|
||||
{
|
||||
flag: append ? 'a' : 'w',
|
||||
},
|
||||
);
|
||||
const logFilePath = `${path}/${fileName}.json`;
|
||||
|
||||
await fs.writeFile(logFilePath, JSON.stringify(data, null, 2), {
|
||||
flag: append ? 'a' : 'w',
|
||||
});
|
||||
|
||||
const absoluteLogFilePath = joinPath(process.cwd(), logFilePath);
|
||||
|
||||
return absoluteLogFilePath;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`Error writing to file ${path}/${fileName}.json: ${err?.message}`,
|
||||
);
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,10 @@ import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { IDField } from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import {
|
||||
WorkspaceMemberDateFormatEnum,
|
||||
WorkspaceMemberTimeFormatEnum,
|
||||
} from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
|
||||
@ObjectType('FullName')
|
||||
export class FullName {
|
||||
@ -27,6 +31,15 @@ export class WorkspaceMember {
|
||||
@Field({ nullable: true })
|
||||
avatarUrl: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
@Field({ nullable: true })
|
||||
locale: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
timeZone: string;
|
||||
|
||||
@Field(() => WorkspaceMemberDateFormatEnum, { nullable: true })
|
||||
dateFormat: WorkspaceMemberDateFormatEnum;
|
||||
|
||||
@Field(() => WorkspaceMemberTimeFormatEnum, { nullable: true })
|
||||
timeFormat: WorkspaceMemberTimeFormatEnum;
|
||||
}
|
||||
|
||||
@ -70,6 +70,9 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
firstName: workspaceMembers[0].nameFirstName,
|
||||
lastName: workspaceMembers[0].nameLastName,
|
||||
};
|
||||
userWorkspaceMember.timeZone = workspaceMembers[0].timeZone;
|
||||
userWorkspaceMember.dateFormat = workspaceMembers[0].dateFormat;
|
||||
userWorkspaceMember.timeFormat = workspaceMembers[0].timeFormat;
|
||||
|
||||
return userWorkspaceMember;
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
import chalk from 'chalk';
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { WorkspaceHealthMode } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-options.interface';
|
||||
import { WorkspaceHealthFixKind } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-fix-kind.interface';
|
||||
import { WorkspaceHealthMode } from 'src/engine/workspace-manager/workspace-health/interfaces/workspace-health-options.interface';
|
||||
|
||||
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
||||
import { CommandLogger } from 'src/command/command-logger';
|
||||
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
||||
|
||||
interface WorkspaceHealthCommandOptions {
|
||||
workspaceId: string;
|
||||
@ -48,13 +48,20 @@ export class WorkspaceHealthCommand extends CommandRunner {
|
||||
chalk.red(`Workspace is not healthy, found ${issues.length} issues`),
|
||||
);
|
||||
|
||||
if (options.dryRun) {
|
||||
await this.commandLogger.writeLog(
|
||||
`workspace-health-issues-${options.workspaceId}`,
|
||||
issues,
|
||||
for (let issueIndex = 0; issueIndex < issues.length; issueIndex++) {
|
||||
this.logger.log(
|
||||
chalk.red(`Issue #${issueIndex + 1} : ${issues[issueIndex].message}`),
|
||||
);
|
||||
this.logger.log(chalk.yellow('Issues written to log'));
|
||||
}
|
||||
|
||||
const logFilePath = await this.commandLogger.writeLog(
|
||||
`workspace-health-issues-${options.workspaceId}`,
|
||||
issues,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
chalk.yellow(`Issues written to log at : ${logFilePath}`),
|
||||
);
|
||||
}
|
||||
|
||||
if (options.fix) {
|
||||
|
||||
@ -369,6 +369,9 @@ export const WORKSPACE_MEMBER_STANDARD_FIELD_IDS = {
|
||||
calendarEventParticipants: '20202020-0dbc-4841-9ce1-3e793b5b3512',
|
||||
timelineActivities: '20202020-e15b-47b8-94fe-8200e3c66615',
|
||||
auditLogs: '20202020-2f54-4739-a5e2-99563385e83d',
|
||||
timeZone: '20202020-2d33-4c21-a86e-5943b050dd54',
|
||||
dateFormat: '20202020-af13-4e11-b1e7-b8cf5ea13dc0',
|
||||
timeFormat: '20202020-8acb-4cf8-a851-a6ed443c8d81',
|
||||
};
|
||||
|
||||
export const CUSTOM_OBJECT_STANDARD_FIELD_IDS = {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
|
||||
|
||||
import { FullNameMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type';
|
||||
@ -27,6 +29,30 @@ import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/
|
||||
import { AuditLogWorkspaceEntity } from 'src/modules/timeline/standard-objects/audit-log.workspace-entity';
|
||||
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
|
||||
|
||||
export enum WorkspaceMemberDateFormatEnum {
|
||||
SYSTEM = 'SYSTEM',
|
||||
MONTH_FIRST = 'MONTH_FIRST',
|
||||
DAY_FIRST = 'DAY_FIRST',
|
||||
YEAR_FIRST = 'YEAR_FIRST',
|
||||
}
|
||||
|
||||
export enum WorkspaceMemberTimeFormatEnum {
|
||||
SYSTEM = 'SYSTEM',
|
||||
HOUR_12 = 'HOUR_12',
|
||||
HOUR_24 = 'HOUR_24',
|
||||
}
|
||||
|
||||
registerEnumType(WorkspaceMemberTimeFormatEnum, {
|
||||
name: 'WorkspaceMemberTimeFormatEnum',
|
||||
description: 'Time time as Military, Standard or system as default',
|
||||
});
|
||||
|
||||
registerEnumType(WorkspaceMemberDateFormatEnum, {
|
||||
name: 'WorkspaceMemberDateFormatEnum',
|
||||
description:
|
||||
'Date format as Month first, Day first, Year first or system as default',
|
||||
});
|
||||
|
||||
@WorkspaceEntity({
|
||||
standardId: STANDARD_OBJECT_IDS.workspaceMember,
|
||||
namePlural: 'workspaceMembers',
|
||||
@ -242,4 +268,80 @@ export class WorkspaceMemberWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
@WorkspaceIsNullable()
|
||||
@WorkspaceIsSystem()
|
||||
auditLogs: Relation<AuditLogWorkspaceEntity[]>;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.timeZone,
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'Time zone',
|
||||
defaultValue: "'system'",
|
||||
description: 'User time zone',
|
||||
icon: 'IconTimezone',
|
||||
})
|
||||
timeZone: string;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.dateFormat,
|
||||
type: FieldMetadataType.SELECT,
|
||||
label: 'Date format',
|
||||
description: "User's preferred date format",
|
||||
icon: 'IconCalendarEvent',
|
||||
options: [
|
||||
{
|
||||
value: WorkspaceMemberDateFormatEnum.SYSTEM,
|
||||
label: 'System',
|
||||
position: 0,
|
||||
color: 'turquoise',
|
||||
},
|
||||
{
|
||||
value: WorkspaceMemberDateFormatEnum.MONTH_FIRST,
|
||||
label: 'Month First',
|
||||
position: 1,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
value: WorkspaceMemberDateFormatEnum.DAY_FIRST,
|
||||
label: 'Day First',
|
||||
position: 2,
|
||||
color: 'purple',
|
||||
},
|
||||
{
|
||||
value: WorkspaceMemberDateFormatEnum.YEAR_FIRST,
|
||||
label: 'Year First',
|
||||
position: 3,
|
||||
color: 'sky',
|
||||
},
|
||||
],
|
||||
defaultValue: `'${WorkspaceMemberDateFormatEnum.SYSTEM}'`,
|
||||
})
|
||||
dateFormat: string;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.timeFormat,
|
||||
type: FieldMetadataType.SELECT,
|
||||
label: 'Time format',
|
||||
description: "User's preferred time format",
|
||||
icon: 'IconClock2',
|
||||
options: [
|
||||
{
|
||||
value: WorkspaceMemberTimeFormatEnum.SYSTEM,
|
||||
label: 'System',
|
||||
position: 0,
|
||||
color: 'sky',
|
||||
},
|
||||
{
|
||||
value: WorkspaceMemberTimeFormatEnum.HOUR_24,
|
||||
label: '24HRS',
|
||||
position: 1,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
value: WorkspaceMemberTimeFormatEnum.HOUR_12,
|
||||
label: '12HRS',
|
||||
position: 2,
|
||||
color: 'purple',
|
||||
},
|
||||
],
|
||||
defaultValue: `'${WorkspaceMemberTimeFormatEnum.SYSTEM}'`,
|
||||
})
|
||||
timeFormat: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user