5899 display a banner to alert users which need to reconnect their account (#6301)

Closes #5899

<img width="1280" alt="Index - banner"
src="https://github.com/twentyhq/twenty/assets/71827178/313cf20d-eb34-496a-8c7c-7589fbd55954">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
bosiraphael
2024-07-27 18:34:52 +02:00
committed by GitHub
parent d0db3b765f
commit 6728e40256
48 changed files with 910 additions and 147 deletions

View File

@ -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;

View File

@ -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<CurrentUser | null>({

View File

@ -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 && (
<InformationBannerAccountToReconnect
accountIdToReconnect={firstAccountIdToReconnect}
/>
)}
</>
);
};

View File

@ -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<ConnectedAccount>({
objectNameSingular: CoreObjectNameSingular.ConnectedAccount,
objectRecordId: accountIdToReconnect,
});
const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth();
if (!accountToReconnect?.record) {
return null;
}
return (
<Banner>
Sync lost with mailbox {accountToReconnect?.record?.handle}. Please
reconnect for updates:
<Button
variant="secondary"
title="Reconnect"
Icon={IconRefresh}
size="small"
inverted
onClick={() => triggerGoogleApisOAuth()}
/>
</Banner>
);
};

View File

@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
import { InformationBanner } from '@/information-banner/InformationBanner';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
@ -125,6 +126,7 @@ export const RecordIndexContainer = ({
return (
<StyledContainer>
<InformationBanner />
<RecordFieldValueSelectorContextProvider>
<SpreadsheetImportProvider>
<StyledContainerWithPadding>

View File

@ -51,8 +51,8 @@ export const SettingsAccountsCalendarChannelsGeneral = () => {
startsAt: exampleStartDate.toISOString(),
conferenceSolution: '',
conferenceLink: {
label: '',
url: '',
primaryLinkLabel: '',
primaryLinkUrl: '',
},
description: '',
isCanceled: false,

View File

@ -1,22 +0,0 @@
import styled from '@emotion/styled';
const StyledBanner = styled.div`
align-items: center;
backdrop-filter: blur(5px);
background: ${({ theme }) => theme.color.blue};
display: flex;
gap: ${({ theme }) => theme.spacing(3)};
height: 40px;
justify-content: center;
padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing(3)};
width: 100%;
color: ${({ theme }) => theme.font.color.inverted};
font-family: Inter;
font-size: ${({ theme }) => theme.font.size.md};
font-style: normal;
font-weight: ${({ theme }) => theme.font.weight.medium};
line-height: 150%;
box-sizing: border-box;
`;
export { StyledBanner as Banner };

View File

@ -1,34 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator, IconRefresh } from 'twenty-ui';
import { Button } from '@/ui/input/button/components/Button';
import { Banner } from '../Banner';
const meta: Meta<typeof Banner> = {
title: 'UI/Layout/Banner/Banner',
component: Banner,
decorators: [ComponentDecorator],
render: (args) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<Banner {...args}>
Sync lost with mailbox hello@twenty.com. Please reconnect for updates:
<Button
variant="secondary"
title="Reconnect"
Icon={IconRefresh}
size="small"
inverted
/>
</Banner>
),
argTypes: {
as: { control: false },
theme: { control: false },
},
};
export default meta;
type Story = StoryObj<typeof Banner>;
export const Default: Story = {};

View File

@ -1,9 +1,10 @@
import { JSX } from 'react';
import styled from '@emotion/styled';
import { JSX } from 'react';
import { IconComponent } from 'twenty-ui';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { InformationBanner } from '@/information-banner/InformationBanner';
import { PageBody } from './PageBody';
import { PageHeader } from './PageHeader';
@ -32,7 +33,10 @@ export const SubMenuTopBarContainer = ({
return (
<StyledContainer isMobile={isMobile} className={className}>
{isMobile && <PageHeader title={title} Icon={Icon} />}
<PageBody>{children}</PageBody>
<PageBody>
<InformationBanner />
{children}
</PageBody>
</StyledContainer>
);
};

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';

View File

@ -49,5 +49,6 @@ export const USER_QUERY_FRAGMENT = gql`
domainName
}
}
userVars
}
`;