Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add timezone and datetime formatting on workspace member level #5699

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
fb27840
detect browser date time format
AdityaPimpalkar May 31, 2024
1af562d
move constants and utils from settings to workspace member
AdityaPimpalkar May 31, 2024
363ca8d
introduce date time states
AdityaPimpalkar May 31, 2024
8c41f58
move DateTimeSettings to profile section
AdityaPimpalkar May 31, 2024
820691d
add timeZone, dateFormat and timeFormat columns to workspace member
AdityaPimpalkar Jun 2, 2024
e4673d9
add utils and enums to format date time labels
AdityaPimpalkar Jun 2, 2024
45f18aa
update WorkspaceMember type on front end
AdityaPimpalkar Jun 2, 2024
a8f5181
add date time fields to currentWorkspaceMemberState on sign in
AdityaPimpalkar Jun 2, 2024
0fcfe94
format using currentWorkspaceMember
AdityaPimpalkar Jun 2, 2024
5905659
update mock data
AdityaPimpalkar Jun 2, 2024
eb3865d
lint fix
AdityaPimpalkar Jun 2, 2024
e48a87c
values as enums for dateformat and timeformat
AdityaPimpalkar Jun 6, 2024
82832ae
Merge branch 'main' into datetime-formatting
AdityaPimpalkar Jun 6, 2024
b082718
gql codegen
AdityaPimpalkar Jun 6, 2024
cfbef7b
add prefix 'preferred'
AdityaPimpalkar Jun 11, 2024
617a7c9
lint fix
AdityaPimpalkar Jun 12, 2024
8c9412a
rename DateFormat to WorkspaceMemberDateFormatEnum
AdityaPimpalkar Jun 15, 2024
050fefe
remove prefix "preferred"
AdityaPimpalkar Jun 15, 2024
00ed339
add methods to convert to workspace enums
AdityaPimpalkar Jun 15, 2024
72ee43b
add SYSTEM to DateFormat and TimeFormat enums
AdityaPimpalkar Jun 15, 2024
81807df
adapt workspace member date time format across datetime display fields
AdityaPimpalkar Jun 15, 2024
1feebb1
Merge branch 'main' into datetime-formatting
AdityaPimpalkar Jun 15, 2024
6739486
graphql codegen
AdityaPimpalkar Jun 15, 2024
3f2ad1b
fix tests
AdityaPimpalkar Jun 16, 2024
5bc14e7
ci fix
AdityaPimpalkar Jun 16, 2024
b8615d8
lint fix
AdityaPimpalkar Jun 16, 2024
96c9aad
import fix
AdityaPimpalkar Jun 16, 2024
c7e0deb
fix storybook performance test
AdityaPimpalkar Jun 16, 2024
09e2205
sort options as per system preference
AdityaPimpalkar Jun 19, 2024
88bfc12
format date as per timezone
AdityaPimpalkar Jun 19, 2024
fdba8bf
fix test
AdityaPimpalkar Jun 19, 2024
01131c9
move DateTimeSettings from Profile to Apperance
AdityaPimpalkar Jun 19, 2024
62747d7
add dateTimeFormat property to RecordTableContext
AdityaPimpalkar Jun 19, 2024
3102737
pass datetime props to DateTimeDisplay
AdityaPimpalkar Jun 19, 2024
d00c362
fix typo
AdityaPimpalkar Jun 19, 2024
44f3349
fix timezone in test
AdityaPimpalkar Jun 19, 2024
0dc269c
lint fix
AdityaPimpalkar Jun 19, 2024
85e39b5
introduce dateTimeFormatState
AdityaPimpalkar Jun 19, 2024
a6ff7e5
refacto DateTimeSettings
AdityaPimpalkar Jun 19, 2024
561414b
add object Id to export columns
AdityaPimpalkar Jun 20, 2024
420d251
undo previous commit
AdityaPimpalkar Jun 20, 2024
e56457a
add "System settings" as dropdown parameter on DateTimeSettings
AdityaPimpalkar Jun 20, 2024
e706b00
lint fix
AdityaPimpalkar Jun 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions packages/twenty-front/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ export type CursorPaging = {
last?: InputMaybe<Scalars['Int']>;
};

/** Defines date formats with Month First, Day First, Year First order or System. */
export enum DateFormat {
DMmmYyyy = 'D_MMM_YYYY',
MmmDYyyy = 'MMM_D_YYYY',
System = 'SYSTEM',
YyyyMmmD = 'YYYY_MMM_D'
}

export type DeleteOneObjectInput = {
/** The id of the record to delete. */
id: Scalars['UUID'];
Expand Down Expand Up @@ -584,6 +592,7 @@ export type RemoteServer = {
foreignDataWrapperOptions?: Maybe<Scalars['JSON']>;
foreignDataWrapperType: Scalars['String'];
id: Scalars['ID'];
label: Scalars['String'];
schema?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
userMappingOptions?: Maybe<UserMappingOptionsUser>;
Expand Down Expand Up @@ -640,6 +649,13 @@ export type Telemetry = {
enabled: Scalars['Boolean'];
};

/** Defines time formats in Standard / Military or System. */
export enum TimeFormat {
HhMm = 'HH_mm',
System = 'SYSTEM',
HMmAa = 'h_mm_aa'
}

export type TimelineCalendarEvent = {
__typename?: 'TimelineCalendarEvent';
conferenceLink: LinkMetadata;
Expand Down Expand Up @@ -879,9 +895,12 @@ export type WorkspaceMember = {
__typename?: 'WorkspaceMember';
avatarUrl?: Maybe<Scalars['String']>;
colorScheme: Scalars['String'];
dateFormat: DateFormat;
id: Scalars['UUID'];
locale: Scalars['String'];
name: FullName;
timeFormat: TimeFormat;
timeZone: Scalars['String'];
};

export type Field = {
Expand Down Expand Up @@ -1108,7 +1127,7 @@ export type ImpersonateMutationVariables = Exact<{
}>;


export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, timeZone: string, dateFormat: DateFormat, timeFormat: TimeFormat, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };

export type RenewTokenMutationVariables = Exact<{
appToken: Scalars['String'];
Expand Down Expand Up @@ -1140,7 +1159,7 @@ export type VerifyMutationVariables = Exact<{
}>;


export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, timeZone: string, dateFormat: DateFormat, timeFormat: TimeFormat, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };

export type CheckUserExistsQueryVariables = Exact<{
email: Scalars['String'];
Expand Down Expand Up @@ -1189,7 +1208,7 @@ export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;

export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, chromeExtensionId?: string | null, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null } } };

export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, timeZone: string, dateFormat: DateFormat, timeFormat: TimeFormat, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };

export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;

Expand All @@ -1206,7 +1225,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;


export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null } | null }> } };
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, timeZone: string, dateFormat: DateFormat, timeFormat: TimeFormat, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null } | null }> } };

export type AddUserToWorkspaceMutationVariables = Exact<{
inviteHash: Scalars['String'];
Expand Down Expand Up @@ -1359,6 +1378,9 @@ export const UserQueryFragmentFragmentDoc = gql`
colorScheme
avatarUrl
locale
timeZone
dateFormat
timeFormat
}
defaultWorkspace {
id
Expand Down Expand Up @@ -2420,6 +2442,9 @@ export const GetCurrentUserDocument = gql`
colorScheme
avatarUrl
locale
timeZone
dateFormat
timeFormat
}
defaultWorkspace {
id
Expand Down
9 changes: 9 additions & 0 deletions packages/twenty-front/src/modules/auth/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilled
import { supportChatState } from '@/client-config/states/supportChatState';
import { telemetryState } from '@/client-config/states/telemetryState';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { detectTimeZone } from '@/workspace-member/utils/detectTimeZone';
import { formatDateLabel } from '@/workspace-member/utils/formatDateLabel';
import { formatTimeLabel } from '@/workspace-member/utils/formatTimeLabel';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import {
useChallengeMutation,
Expand Down Expand Up @@ -101,6 +104,12 @@ export const useAuth = () => {
if (isDefined(user.workspaceMember)) {
workspaceMember = {
...user.workspaceMember,
timeZone:
user.workspaceMember.timeZone !== 'system'
? user.workspaceMember.timeZone
: detectTimeZone(),
dateFormat: formatDateLabel(user.workspaceMember.dateFormat),
timeFormat: formatTimeLabel(user.workspaceMember.timeFormat),
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
};
setCurrentWorkspaceMember(workspaceMember);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { useState } from 'react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';

import { SettingsAccountsCalendarDateFormatSelect } from '@/settings/accounts/components/SettingsAccountsCalendarDateFormatSelect';
import { SettingsAccountsCalendarTimeFormatSelect } from '@/settings/accounts/components/SettingsAccountsCalendarTimeFormatSelect';
import { SettingsAccountsCalendarTimeZoneSelect } from '@/settings/accounts/components/SettingsAccountsCalendarTimeZoneSelect';
import { DateFormat } from '@/settings/accounts/constants/DateFormat';
import { TimeFormat } from '@/settings/accounts/constants/TimeFormat';
import { detectTimeZone } from '@/settings/accounts/utils/detectTimeZone';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { DateTimeSettingsDateFormatSelect } from '@/settings/profile/components/DateTimeSettingsDateFormatSelect';
import { DateTimeSettingsTimeFormatSelect } from '@/settings/profile/components/DateTimeSettingsTimeFormatSelect';
import { DateTimeSettingsTimeZoneSelect } from '@/settings/profile/components/DateTimeSettingsTimeZoneSelect';
import { DateFormat } from '@/workspace-member/constants/DateFormat';
import { TimeFormat } from '@/workspace-member/constants/TimeFormat';
import { detectTimeZone } from '@/workspace-member/utils/detectTimeZone';

const StyledContainer = styled.div`
display: flex;
Expand All @@ -15,27 +17,27 @@ const StyledContainer = styled.div`
`;

export const SettingsAccountsCalendarDisplaySettings = () => {
// TODO: use the user's saved time zone. If undefined, default it with the user's detected time zone.
const [timeZone, setTimeZone] = useState(detectTimeZone());
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);

// TODO: use the user's saved date format.
const [dateFormat, setDateFormat] = useState(DateFormat.US);

// TODO: use the user's saved time format.
const [timeFormat, setTimeFormat] = useState(TimeFormat['24h']);
const [timeZone, setTimeZone] = useState(
currentWorkspaceMember?.timeZone ?? detectTimeZone(),
);
const [dateFormat, setDateFormat] = useState(
currentWorkspaceMember?.dateFormat ?? DateFormat.MonthFirst,
);
const [timeFormat, setTimeFormat] = useState(
currentWorkspaceMember?.timeFormat ?? TimeFormat.Military,
);

return (
<StyledContainer>
<SettingsAccountsCalendarTimeZoneSelect
value={timeZone}
onChange={setTimeZone}
/>
<SettingsAccountsCalendarDateFormatSelect
<DateTimeSettingsTimeZoneSelect value={timeZone} onChange={setTimeZone} />
<DateTimeSettingsDateFormatSelect
value={dateFormat}
onChange={setDateFormat}
timeZone={timeZone}
/>
<SettingsAccountsCalendarTimeFormatSelect
<DateTimeSettingsTimeFormatSelect
value={timeFormat}
onChange={setTimeFormat}
timeZone={timeZone}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading