diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 45e025a09..c33006259 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -20,6 +20,8 @@ export type Scalars = { DateTime: { input: any; output: any; } /** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ JSON: { input: any; output: any; } + /** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ + JSONObject: { input: any; output: any; } /** A UUID scalar type */ UUID: { input: any; output: any; } /** The `Upload` scalar type represents a file upload. */ @@ -381,6 +383,13 @@ export type LinkMetadata = { url: Scalars['String']['output']; }; +export type LinksMetadata = { + __typename?: 'LinksMetadata'; + primaryLinkLabel: Scalars['String']['output']; + primaryLinkUrl: Scalars['String']['output']; + secondaryLinks?: Maybe>; +}; + export type LoginToken = { __typename?: 'LoginToken'; loginToken: AuthToken; @@ -1025,7 +1034,7 @@ export type Telemetry = { export type TimelineCalendarEvent = { __typename?: 'TimelineCalendarEvent'; - conferenceLink: LinkMetadata; + conferenceLink: LinksMetadata; conferenceSolution: Scalars['String']['output']; description: Scalars['String']['output']; endsAt: Scalars['DateTime']['output']; @@ -1186,12 +1195,9 @@ export type User = { lastName: Scalars['String']['output']; onboardingStatus?: Maybe; passwordHash?: Maybe; - /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ - passwordResetToken?: Maybe; - /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ - passwordResetTokenExpiresAt?: Maybe; supportUserHash?: Maybe; updatedAt: Scalars['DateTime']['output']; + userVars: Scalars['JSONObject']['output']; workspaceMember?: Maybe; workspaces: Array; }; @@ -1412,6 +1418,7 @@ export type ServerlessFunction = { createdAt: Scalars['DateTime']['output']; id: Scalars['UUID']['output']; name: Scalars['String']['output']; + sourceCodeHash: Scalars['String']['output']; syncStatus: ServerlessFunctionSyncStatus; updatedAt: Scalars['DateTime']['output']; }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 4cfb626e9..4bf49074e 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -16,6 +16,7 @@ export type Scalars = { ConnectionCursor: any; DateTime: string; JSON: any; + JSONObject: any; UUID: any; Upload: any; }; @@ -280,6 +281,13 @@ export type LinkMetadata = { url: Scalars['String']; }; +export type LinksMetadata = { + __typename?: 'LinksMetadata'; + primaryLinkLabel: Scalars['String']; + primaryLinkUrl: Scalars['String']; + secondaryLinks?: Maybe>; +}; + export type LoginToken = { __typename?: 'LoginToken'; loginToken: AuthToken; @@ -752,7 +760,7 @@ export type Telemetry = { export type TimelineCalendarEvent = { __typename?: 'TimelineCalendarEvent'; - conferenceLink: LinkMetadata; + conferenceLink: LinksMetadata; conferenceSolution: Scalars['String']; description: Scalars['String']; endsAt: Scalars['DateTime']; @@ -884,12 +892,9 @@ export type User = { lastName: Scalars['String']; onboardingStatus?: Maybe; passwordHash?: Maybe; - /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ - passwordResetToken?: Maybe; - /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ - passwordResetTokenExpiresAt?: Maybe; supportUserHash?: Maybe; updatedAt: Scalars['DateTime']; + userVars: Scalars['JSONObject']; workspaceMember?: Maybe; workspaces: Array; }; @@ -1090,6 +1095,7 @@ export type ServerlessFunction = { createdAt: Scalars['DateTime']; id: Scalars['UUID']; name: Scalars['String']; + sourceCodeHash: Scalars['String']; syncStatus: ServerlessFunctionSyncStatus; updatedAt: Scalars['DateTime']; }; @@ -1228,7 +1234,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, onboardingStatus?: OnboardingStatus | 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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, onboardingStatus?: OnboardingStatus | null, userVars: any, 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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']; @@ -1260,7 +1266,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, onboardingStatus?: OnboardingStatus | 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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, onboardingStatus?: OnboardingStatus | null, userVars: any, 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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']; @@ -1321,7 +1327,7 @@ export type GetAisqlQueryQueryVariables = Exact<{ export type GetAisqlQueryQuery = { __typename?: 'Query', getAISQLQuery: { __typename?: 'AISQLQueryResult', sqlQuery: string, sqlQueryResult?: string | null, queryFailedErrorMessage?: string | null } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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, onboardingStatus?: OnboardingStatus | null, userVars: any, 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | 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; }>; @@ -1338,7 +1344,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, onboardingStatus?: OnboardingStatus | 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: 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, onboardingStatus?: OnboardingStatus | null, userVars: any, 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, activationStatus: string, currentCacheVersion?: string | null, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; export type AddUserToWorkspaceMutationVariables = Exact<{ inviteHash: Scalars['String']; @@ -1523,6 +1529,7 @@ export const UserQueryFragmentFragmentDoc = gql` domainName } } + userVars } `; export const GetTimelineCalendarEventsFromCompanyIdDocument = gql` diff --git a/packages/twenty-front/src/modules/activities/calendar/types/CalendarEvent.ts b/packages/twenty-front/src/modules/activities/calendar/types/CalendarEvent.ts index 9d2eb17ad..bedaa440c 100644 --- a/packages/twenty-front/src/modules/activities/calendar/types/CalendarEvent.ts +++ b/packages/twenty-front/src/modules/activities/calendar/types/CalendarEvent.ts @@ -4,8 +4,8 @@ import { CalendarChannelVisibility } from '~/generated/graphql'; // TODO: use backend CalendarEvent type when ready export type CalendarEvent = { conferenceLink?: { - label: string; - url: string; + primaryLinkLabel: string; + primaryLinkUrl: string; }; description?: string; endsAt?: string; diff --git a/packages/twenty-front/src/modules/auth/states/currentUserState.ts b/packages/twenty-front/src/modules/auth/states/currentUserState.ts index 2aab02507..2feedc94f 100644 --- a/packages/twenty-front/src/modules/auth/states/currentUserState.ts +++ b/packages/twenty-front/src/modules/auth/states/currentUserState.ts @@ -4,7 +4,12 @@ import { User } from '~/generated/graphql'; export type CurrentUser = Pick< User, - 'id' | 'email' | 'supportUserHash' | 'canImpersonate' | 'onboardingStatus' + | 'id' + | 'email' + | 'supportUserHash' + | 'canImpersonate' + | 'onboardingStatus' + | 'userVars' >; export const currentUserState = createState({ diff --git a/packages/twenty-front/src/modules/information-banner/InformationBanner.tsx b/packages/twenty-front/src/modules/information-banner/InformationBanner.tsx new file mode 100644 index 000000000..692774397 --- /dev/null +++ b/packages/twenty-front/src/modules/information-banner/InformationBanner.tsx @@ -0,0 +1,26 @@ +import { currentUserState } from '@/auth/states/currentUserState'; +import { InformationBannerAccountToReconnect } from '@/information-banner/InformationBannerReconnectAccount'; +import { useRecoilValue } from 'recoil'; + +export enum InformationBannerKeys { + ACCOUNTS_TO_RECONNECT = 'ACCOUNTS_TO_RECONNECT', +} + +export const InformationBanner = () => { + const currentUser = useRecoilValue(currentUserState); + + const userVars = currentUser?.userVars; + + const firstAccountIdToReconnect = + userVars?.[InformationBannerKeys.ACCOUNTS_TO_RECONNECT]?.[0]; + + return ( + <> + {firstAccountIdToReconnect && ( + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/information-banner/InformationBannerReconnectAccount.tsx b/packages/twenty-front/src/modules/information-banner/InformationBannerReconnectAccount.tsx new file mode 100644 index 000000000..94064100b --- /dev/null +++ b/packages/twenty-front/src/modules/information-banner/InformationBannerReconnectAccount.tsx @@ -0,0 +1,38 @@ +import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; +import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth'; +import { Button } from '@/ui/input/button/components/Button'; +import { Banner, IconRefresh } from 'twenty-ui'; + +export const InformationBannerAccountToReconnect = ({ + accountIdToReconnect, +}: { + accountIdToReconnect: string; +}) => { + const accountToReconnect = useFindOneRecord({ + objectNameSingular: CoreObjectNameSingular.ConnectedAccount, + objectRecordId: accountIdToReconnect, + }); + + const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth(); + + if (!accountToReconnect?.record) { + return null; + } + + return ( + + Sync lost with mailbox {accountToReconnect?.record?.handle}. Please + reconnect for updates: +