From 5e2673a2a497d3ca4aab59cef7b543b5a40ca1a6 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Wed, 7 Jun 2023 12:48:44 +0200 Subject: [PATCH] Lucas/t 366 on comment drawer when i have comments on the selected (#201) * Fixed right drawer width and shared in theme * Added date packages and tooltip * Added date utils and tests * Added comment thread components * Fixed comment chip * Fix from rebase * Fix from rebase * Fix margin right * Fixed CSS and graphql --- front/package-lock.json | 44 +++++ front/package.json | 4 + front/src/generated/graphql.tsx | 60 +++---- .../components/comments/CellCommentChip.tsx | 2 +- .../components/comments/CommentChip.tsx | 12 +- .../components/comments/CommentHeader.tsx | 80 +++++++++ .../components/comments/CommentThread.tsx | 26 ++- .../components/comments/CommentThreadItem.tsx | 40 +++++ .../__stories__/CommentChip.stories.tsx | 2 +- .../__stories__/CommentHeader.stories.tsx | 67 ++++++++ .../__stories__/CommentTextInput.stories.tsx | 2 +- front/src/modules/comments/services/select.ts | 12 +- .../comments/types/CommentForDrawer.ts | 5 + .../components/CompanyEditableNameCell.tsx | 15 +- .../__tests__/company.interface.test.ts | 3 + .../components/EditablePeopleFullName.tsx | 14 +- .../layout/containers/WithTopBarContainer.tsx | 9 +- .../right-drawer/components/RightDrawer.tsx | 3 +- .../components/RightDrawerBody.tsx | 2 - front/src/modules/ui/layout/styles/themes.ts | 4 + .../modules/users/components/UserAvatar.tsx | 61 +++++++ .../__stories__/UserAvatar.stories.tsx | 40 +++++ .../__tests__/user.interface.test.ts | 4 + .../users/interfaces/user.interface.ts | 5 + .../datetime/__tests__/date-utils.test.ts | 155 ++++++++++++++++++ .../src/modules/utils/datetime/date-utils.ts | 75 +++++++++ front/src/modules/utils/logs/logError.ts | 3 + front/src/testing/graphqlMocks.ts | 12 +- front/src/testing/mock-data/users.ts | 2 + server/src/database/seeds/users.ts | 2 + 30 files changed, 688 insertions(+), 77 deletions(-) create mode 100644 front/src/modules/comments/components/comments/CommentHeader.tsx create mode 100644 front/src/modules/comments/components/comments/CommentThreadItem.tsx create mode 100644 front/src/modules/comments/components/comments/__stories__/CommentHeader.stories.tsx create mode 100644 front/src/modules/comments/types/CommentForDrawer.ts create mode 100644 front/src/modules/users/components/UserAvatar.tsx create mode 100644 front/src/modules/users/components/__stories__/UserAvatar.stories.tsx create mode 100644 front/src/modules/utils/datetime/__tests__/date-utils.test.ts create mode 100644 front/src/modules/utils/datetime/date-utils.ts create mode 100644 front/src/modules/utils/logs/logError.ts diff --git a/front/package-lock.json b/front/package-lock.json index 9c2447bd9..e830065b7 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -17,9 +17,11 @@ "@types/react": "^18.0.25", "@types/react-dom": "^18.0.9", "apollo-link-rest": "^0.9.0", + "date-fns": "^2.30.0", "graphql": "^16.6.0", "jwt-decode": "^3.1.2", "libphonenumber-js": "^1.10.26", + "luxon": "^3.3.0", "react": "^18.2.0", "react-datepicker": "^4.11.0", "react-dom": "^18.2.0", @@ -27,6 +29,7 @@ "react-icons": "^4.8.0", "react-router-dom": "^6.4.4", "react-textarea-autosize": "^8.4.1", + "react-tooltip": "^5.13.1", "recoil": "^0.7.7", "uuid": "^9.0.0", "web-vitals": "^2.1.4" @@ -54,6 +57,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", + "@types/luxon": "^3.3.0", "@types/react-datepicker": "^4.11.2", "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.45.0", @@ -3701,6 +3705,19 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "node_modules/@floating-ui/core": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz", + "integrity": "sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==" + }, + "node_modules/@floating-ui/dom": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.2.9.tgz", + "integrity": "sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ==", + "dependencies": { + "@floating-ui/core": "^1.2.6" + } + }, "node_modules/@graphql-codegen/cli": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-3.3.1.tgz", @@ -11044,6 +11061,12 @@ "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, + "node_modules/@types/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", + "dev": true + }, "node_modules/@types/mdx": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.5.tgz", @@ -28277,6 +28300,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", + "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -33806,6 +33837,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-tooltip": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.13.1.tgz", + "integrity": "sha512-9NstDFdjyy6cIH9zjeT70zXTHlW/TIGCOWQmhkAyqLFeQioLg1FXvb9ec7AxSpn0zyFUkFSLdFYxZRuewti3Aw==", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/front/package.json b/front/package.json index 2b687b55b..8b7b17530 100644 --- a/front/package.json +++ b/front/package.json @@ -12,9 +12,11 @@ "@types/react": "^18.0.25", "@types/react-dom": "^18.0.9", "apollo-link-rest": "^0.9.0", + "date-fns": "^2.30.0", "graphql": "^16.6.0", "jwt-decode": "^3.1.2", "libphonenumber-js": "^1.10.26", + "luxon": "^3.3.0", "react": "^18.2.0", "react-datepicker": "^4.11.0", "react-dom": "^18.2.0", @@ -22,6 +24,7 @@ "react-icons": "^4.8.0", "react-router-dom": "^6.4.4", "react-textarea-autosize": "^8.4.1", + "react-tooltip": "^5.13.1", "recoil": "^0.7.7", "uuid": "^9.0.0", "web-vitals": "^2.1.4" @@ -98,6 +101,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", + "@types/luxon": "^3.3.0", "@types/react-datepicker": "^4.11.2", "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.45.0", diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index e898620c1..5e1cc0804 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -1081,19 +1081,19 @@ export type WorkspaceMember = { workspace: Workspace; }; -export type GetCompanyCountsQueryVariables = Exact<{ +export type GetCompanyCommentsCountQueryVariables = Exact<{ where?: InputMaybe; }>; -export type GetCompanyCountsQuery = { __typename?: 'Query', companies: Array<{ __typename?: 'Company', commentsCount: number }> }; +export type GetCompanyCommentsCountQuery = { __typename?: 'Query', companies: Array<{ __typename?: 'Company', commentsCount: number }> }; -export type GetPeopleCountsQueryVariables = Exact<{ +export type GetPeopleCommentsCountQueryVariables = Exact<{ where?: InputMaybe; }>; -export type GetPeopleCountsQuery = { __typename?: 'Query', people: Array<{ __typename?: 'Person', commentsCount: number }> }; +export type GetPeopleCommentsCountQuery = { __typename?: 'Query', people: Array<{ __typename?: 'Person', commentsCount: number }> }; export type GetCommentThreadsByTargetsQueryVariables = Exact<{ commentThreadTargetIds: Array | Scalars['String']; @@ -1227,8 +1227,8 @@ export type GetUsersQueryVariables = Exact<{ [key: string]: never; }>; export type GetUsersQuery = { __typename?: 'Query', findManyUser: Array<{ __typename?: 'User', id: string }> }; -export const GetCompanyCountsDocument = gql` - query GetCompanyCounts($where: CompanyWhereInput) { +export const GetCompanyCommentsCountDocument = gql` + query GetCompanyCommentsCount($where: CompanyWhereInput) { companies: findManyCompany(where: $where) { commentsCount: _commentCount } @@ -1236,34 +1236,34 @@ export const GetCompanyCountsDocument = gql` `; /** - * __useGetCompanyCountsQuery__ + * __useGetCompanyCommentsCountQuery__ * - * To run a query within a React component, call `useGetCompanyCountsQuery` and pass it any options that fit your needs. - * When your component renders, `useGetCompanyCountsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetCompanyCommentsCountQuery` and pass it any options that fit your needs. + * When your component renders, `useGetCompanyCommentsCountQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useGetCompanyCountsQuery({ + * const { data, loading, error } = useGetCompanyCommentsCountQuery({ * variables: { * where: // value for 'where' * }, * }); */ -export function useGetCompanyCountsQuery(baseOptions?: Apollo.QueryHookOptions) { +export function useGetCompanyCommentsCountQuery(baseOptions?: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(GetCompanyCountsDocument, options); + return Apollo.useQuery(GetCompanyCommentsCountDocument, options); } -export function useGetCompanyCountsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useGetCompanyCommentsCountLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(GetCompanyCountsDocument, options); + return Apollo.useLazyQuery(GetCompanyCommentsCountDocument, options); } -export type GetCompanyCountsQueryHookResult = ReturnType; -export type GetCompanyCountsLazyQueryHookResult = ReturnType; -export type GetCompanyCountsQueryResult = Apollo.QueryResult; -export const GetPeopleCountsDocument = gql` - query GetPeopleCounts($where: PersonWhereInput) { +export type GetCompanyCommentsCountQueryHookResult = ReturnType; +export type GetCompanyCommentsCountLazyQueryHookResult = ReturnType; +export type GetCompanyCommentsCountQueryResult = Apollo.QueryResult; +export const GetPeopleCommentsCountDocument = gql` + query GetPeopleCommentsCount($where: PersonWhereInput) { people: findManyPerson(where: $where) { commentsCount: _commentCount } @@ -1271,32 +1271,32 @@ export const GetPeopleCountsDocument = gql` `; /** - * __useGetPeopleCountsQuery__ + * __useGetPeopleCommentsCountQuery__ * - * To run a query within a React component, call `useGetPeopleCountsQuery` and pass it any options that fit your needs. - * When your component renders, `useGetPeopleCountsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetPeopleCommentsCountQuery` and pass it any options that fit your needs. + * When your component renders, `useGetPeopleCommentsCountQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useGetPeopleCountsQuery({ + * const { data, loading, error } = useGetPeopleCommentsCountQuery({ * variables: { * where: // value for 'where' * }, * }); */ -export function useGetPeopleCountsQuery(baseOptions?: Apollo.QueryHookOptions) { +export function useGetPeopleCommentsCountQuery(baseOptions?: Apollo.QueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(GetPeopleCountsDocument, options); + return Apollo.useQuery(GetPeopleCommentsCountDocument, options); } -export function useGetPeopleCountsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { +export function useGetPeopleCommentsCountLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(GetPeopleCountsDocument, options); + return Apollo.useLazyQuery(GetPeopleCommentsCountDocument, options); } -export type GetPeopleCountsQueryHookResult = ReturnType; -export type GetPeopleCountsLazyQueryHookResult = ReturnType; -export type GetPeopleCountsQueryResult = Apollo.QueryResult; +export type GetPeopleCommentsCountQueryHookResult = ReturnType; +export type GetPeopleCommentsCountLazyQueryHookResult = ReturnType; +export type GetPeopleCommentsCountQueryResult = Apollo.QueryResult; export const GetCommentThreadsByTargetsDocument = gql` query GetCommentThreadsByTargets($commentThreadTargetIds: [String!]!) { findManyCommentThreads( diff --git a/front/src/modules/comments/components/comments/CellCommentChip.tsx b/front/src/modules/comments/components/comments/CellCommentChip.tsx index a0641916d..14b17422d 100644 --- a/front/src/modules/comments/components/comments/CellCommentChip.tsx +++ b/front/src/modules/comments/components/comments/CellCommentChip.tsx @@ -4,7 +4,7 @@ import { CommentChip, CommentChipProps } from './CommentChip'; const StyledCellWrapper = styled.div` position: relative; - right: 38px; + right: 34px; top: -13px; width: 0; height: 0; diff --git a/front/src/modules/comments/components/comments/CommentChip.tsx b/front/src/modules/comments/components/comments/CommentChip.tsx index a55dcc82f..66e9124e3 100644 --- a/front/src/modules/comments/components/comments/CommentChip.tsx +++ b/front/src/modules/comments/components/comments/CommentChip.tsx @@ -9,18 +9,18 @@ export type CommentChipProps = { const StyledChip = styled.div` height: 26px; - min-width: 34px; + width: fit-content; - padding-left: 2px; - padding-right: 2px; + padding-left: 4px; + padding-right: 4px; display: flex; align-items: center; justify-content: center; flex-direction: row; - gap: 2px; + gap: 4px; - background: ${(props) => props.theme.secondaryBackgroundTransparent}; + background: ${(props) => props.theme.primaryBackgroundTransparent}; backdrop-filter: blur(6px); border-radius: ${(props) => props.theme.borderRadius}; @@ -53,7 +53,7 @@ export function CommentChip({ count, onClick }: CommentChipProps) { return ( {formattedCount} - + ); } diff --git a/front/src/modules/comments/components/comments/CommentHeader.tsx b/front/src/modules/comments/components/comments/CommentHeader.tsx new file mode 100644 index 000000000..b8612075a --- /dev/null +++ b/front/src/modules/comments/components/comments/CommentHeader.tsx @@ -0,0 +1,80 @@ +import { Tooltip } from 'react-tooltip'; +import styled from '@emotion/styled'; + +import { UserAvatar } from '@/users/components/UserAvatar'; +import { + beautifyExactDate, + beautifyPastDateRelativeToNow, +} from '@/utils/datetime/date-utils'; + +type OwnProps = { + avatarUrl: string | null | undefined; + username: string; + createdAt: Date; +}; + +const StyledContainer = styled.div` + display: flex; + align-items: center; + + flex-direction: row; + + justify-content: flex-start; + + padding: ${(props) => props.theme.spacing(1)}; + + gap: ${(props) => props.theme.spacing(1)}; +`; + +const StyledName = styled.div` + font-size: 13px; + font-weight: 400; + color: ${(props) => props.theme.text80}; +`; + +const StyledDate = styled.div` + font-size: 12px; + font-weight: 400; + color: ${(props) => props.theme.text30}; + + padding-top: 1.5px; + + margin-left: ${(props) => props.theme.spacing(1)}; +`; + +const StyledTooltip = styled(Tooltip)` + padding: 8px; +`; + +export function CommentHeader({ avatarUrl, username, createdAt }: OwnProps) { + const beautifiedCreatedAt = beautifyPastDateRelativeToNow(createdAt); + const exactCreatedAt = beautifyExactDate(createdAt); + const showDate = beautifiedCreatedAt !== ''; + + const capitalizedFirstUsernameLetter = + username !== '' ? username.toLocaleUpperCase()[0] : ''; + + return ( + + + {username} + {showDate && ( + <> + + {beautifiedCreatedAt} + + + + )} + + ); +} diff --git a/front/src/modules/comments/components/comments/CommentThread.tsx b/front/src/modules/comments/components/comments/CommentThread.tsx index df6eca6ae..67a322d4b 100644 --- a/front/src/modules/comments/components/comments/CommentThread.tsx +++ b/front/src/modules/comments/components/comments/CommentThread.tsx @@ -1,27 +1,37 @@ +import styled from '@emotion/styled'; + import { CommentThreadForDrawer } from '@/comments/types/CommentThreadForDrawer'; import { CommentTextInput } from './CommentTextInput'; +import { CommentThreadItem } from './CommentThreadItem'; type OwnProps = { commentThread: CommentThreadForDrawer; }; +const StyledContainer = styled.div` + display: flex; + align-items: flex-start; + + flex-direction: column; + + justify-content: flex-start; + + gap: ${(props) => props.theme.spacing(4)}; + padding: ${(props) => props.theme.spacing(2)}; +`; + export function CommentThread({ commentThread }: OwnProps) { function handleSendComment(text: string) { console.log(text); } return ( -
+ {commentThread.comments?.map((comment) => ( -
-
- {comment.author?.displayName} - {comment.createdAt} -
-
{comment.body}
-
+ ))} -
+ ); } diff --git a/front/src/modules/comments/components/comments/CommentThreadItem.tsx b/front/src/modules/comments/components/comments/CommentThreadItem.tsx new file mode 100644 index 000000000..6718f9bd7 --- /dev/null +++ b/front/src/modules/comments/components/comments/CommentThreadItem.tsx @@ -0,0 +1,40 @@ +import styled from '@emotion/styled'; + +import { CommentForDrawer } from '@/comments/types/CommentForDrawer'; + +import { CommentHeader } from './CommentHeader'; + +type OwnProps = { + comment: CommentForDrawer; +}; + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + gap: ${(props) => props.theme.spacing(1)}; +`; + +const StyledCommentBody = styled.div` + font-size: ${(props) => props.theme.fontSizeMedium}; + line-height: 19.5px; + + text-align: left; + padding-left: 24px; + + color: ${(props) => props.theme.text60}; +`; + +export function CommentThreadItem({ comment }: OwnProps) { + return ( + + + {comment.body} + + ); +} diff --git a/front/src/modules/comments/components/comments/__stories__/CommentChip.stories.tsx b/front/src/modules/comments/components/comments/__stories__/CommentChip.stories.tsx index 7ddf40bf2..85015dbb5 100644 --- a/front/src/modules/comments/components/comments/__stories__/CommentChip.stories.tsx +++ b/front/src/modules/comments/components/comments/__stories__/CommentChip.stories.tsx @@ -7,7 +7,7 @@ import { CellCommentChip } from '../CellCommentChip'; import { CommentChip } from '../CommentChip'; const meta: Meta = { - title: 'Components/CellCommentChip', + title: 'Components/Comments/CellCommentChip', component: CellCommentChip, }; diff --git a/front/src/modules/comments/components/comments/__stories__/CommentHeader.stories.tsx b/front/src/modules/comments/components/comments/__stories__/CommentHeader.stories.tsx new file mode 100644 index 000000000..bc6c9add7 --- /dev/null +++ b/front/src/modules/comments/components/comments/__stories__/CommentHeader.stories.tsx @@ -0,0 +1,67 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { DateTime } from 'luxon'; + +import { mockedUsersData } from '~/testing/mock-data/users'; +import { getRenderWrapperForComponent } from '~/testing/renderWrappers'; + +import { CommentHeader } from '../CommentHeader'; + +const meta: Meta = { + title: 'Components/Comments/CommentHeader', + component: CommentHeader, +}; + +export default meta; +type Story = StoryObj; + +const mockUser = mockedUsersData[0]; + +export const Default: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const FewDaysAgo: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const FewMonthsAgo: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const FewYearsAgo: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const WithoutAvatar: Story = { + render: getRenderWrapperForComponent( + , + ), +}; diff --git a/front/src/modules/comments/components/comments/__stories__/CommentTextInput.stories.tsx b/front/src/modules/comments/components/comments/__stories__/CommentTextInput.stories.tsx index 88210be51..1b7239bb6 100644 --- a/front/src/modules/comments/components/comments/__stories__/CommentTextInput.stories.tsx +++ b/front/src/modules/comments/components/comments/__stories__/CommentTextInput.stories.tsx @@ -6,7 +6,7 @@ import { getRenderWrapperForComponent } from '~/testing/renderWrappers'; import { CommentTextInput } from '../CommentTextInput'; const meta: Meta = { - title: 'Components/CommentTextInput', + title: 'Components/Comments/CommentTextInput', component: CommentTextInput, argTypes: { onSend: { diff --git a/front/src/modules/comments/services/select.ts b/front/src/modules/comments/services/select.ts index de08a0837..19828c9fb 100644 --- a/front/src/modules/comments/services/select.ts +++ b/front/src/modules/comments/services/select.ts @@ -1,12 +1,12 @@ import { gql } from '@apollo/client'; import { - useGetCompanyCountsQuery, - useGetPeopleCountsQuery, + useGetCompanyCommentsCountQuery, + useGetPeopleCommentsCountQuery, } from '../../../generated/graphql'; export const GET_COMPANY_COMMENT_COUNT = gql` - query GetCompanyCounts($where: CompanyWhereInput) { + query GetCompanyCommentsCount($where: CompanyWhereInput) { companies: findManyCompany(where: $where) { commentsCount: _commentCount } @@ -14,14 +14,14 @@ export const GET_COMPANY_COMMENT_COUNT = gql` `; export const useCompanyCommentsCountQuery = (companyId: string) => { - const { data, ...rest } = useGetCompanyCountsQuery({ + const { data, ...rest } = useGetCompanyCommentsCountQuery({ variables: { where: { id: { equals: companyId } } }, }); return { ...rest, data: data?.companies[0].commentsCount }; }; export const GET_PEOPLE_COMMENT_COUNT = gql` - query GetPeopleCounts($where: PersonWhereInput) { + query GetPeopleCommentsCount($where: PersonWhereInput) { people: findManyPerson(where: $where) { commentsCount: _commentCount } @@ -29,7 +29,7 @@ export const GET_PEOPLE_COMMENT_COUNT = gql` `; export const usePeopleCommentsCountQuery = (personId: string) => { - const { data, ...rest } = useGetPeopleCountsQuery({ + const { data, ...rest } = useGetPeopleCommentsCountQuery({ variables: { where: { id: { equals: personId } } }, }); return { ...rest, data: data?.people[0].commentsCount }; diff --git a/front/src/modules/comments/types/CommentForDrawer.ts b/front/src/modules/comments/types/CommentForDrawer.ts new file mode 100644 index 000000000..95ff1f1d7 --- /dev/null +++ b/front/src/modules/comments/types/CommentForDrawer.ts @@ -0,0 +1,5 @@ +import { CommentThreadForDrawer } from './CommentThreadForDrawer'; + +export type CommentForDrawer = NonNullable< + CommentThreadForDrawer['comments'] +>[0]; diff --git a/front/src/modules/companies/components/CompanyEditableNameCell.tsx b/front/src/modules/companies/components/CompanyEditableNameCell.tsx index 4be2abae3..bfd80f8d5 100644 --- a/front/src/modules/companies/components/CompanyEditableNameCell.tsx +++ b/front/src/modules/companies/components/CompanyEditableNameCell.tsx @@ -1,9 +1,9 @@ +import { CellCommentChip } from '@/comments/components/comments/CellCommentChip'; import { useOpenCommentRightDrawer } from '@/comments/hooks/useOpenCommentRightDrawer'; +import { useCompanyCommentsCountQuery } from '@/comments/services'; import EditableChip from '@/ui/components/editable-cell/types/EditableChip'; import { getLogoUrlFromDomainName } from '@/utils/utils'; -import { CellCommentChip } from '../../comments/components/comments/CellCommentChip'; -import { useCompanyCommentsCountQuery } from '../../comments/services'; import { Company } from '../interfaces/company.interface'; import { updateCompany } from '../services'; @@ -16,7 +16,10 @@ type OwnProps = { export function CompanyEditableNameChipCell({ company }: OwnProps) { const openCommentRightDrawer = useOpenCommentRightDrawer(); - function handleCommentClick() { + function handleCommentClick(event: React.MouseEvent) { + event.preventDefault(); + event.stopPropagation(); + openCommentRightDrawer([ { type: 'Company', @@ -27,6 +30,8 @@ export function CompanyEditableNameChipCell({ company }: OwnProps) { const commentCount = useCompanyCommentsCountQuery(company.id); + const displayCommentCount = !commentCount.loading; + return ( ), diff --git a/front/src/modules/companies/interfaces/__tests__/company.interface.test.ts b/front/src/modules/companies/interfaces/__tests__/company.interface.test.ts index e9300c844..646507e21 100644 --- a/front/src/modules/companies/interfaces/__tests__/company.interface.test.ts +++ b/front/src/modules/companies/interfaces/__tests__/company.interface.test.ts @@ -21,6 +21,7 @@ describe('Company mappers', () => { id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87', email: 'john@example.com', displayName: 'John Doe', + avatarUrl: 'https://example.com/avatar.png', __typename: 'User', }, pipes: [ @@ -47,6 +48,7 @@ describe('Company mappers', () => { __typename: 'users', id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e87', email: 'john@example.com', + avatarUrl: 'https://example.com/avatar.png', displayName: 'John Doe', workspaceMember: undefined, }, @@ -67,6 +69,7 @@ describe('Company mappers', () => { accountOwner: { id: '522d4ec4-c46b-4360-a0a7-df8df170be81', email: 'john@example.com', + avatarUrl: 'https://example.com/avatar.png', displayName: 'John Doe', __typename: 'users', }, diff --git a/front/src/modules/people/components/EditablePeopleFullName.tsx b/front/src/modules/people/components/EditablePeopleFullName.tsx index 8b323814c..e40ebbdbf 100644 --- a/front/src/modules/people/components/EditablePeopleFullName.tsx +++ b/front/src/modules/people/components/EditablePeopleFullName.tsx @@ -2,9 +2,9 @@ import { useState } from 'react'; import styled from '@emotion/styled'; import { CellCommentChip } from '@/comments/components/comments/CellCommentChip'; +import { useOpenCommentRightDrawer } from '@/comments/hooks/useOpenCommentRightDrawer'; import { EditableDoubleText } from '@/ui/components/editable-cell/types/EditableDoubleText'; -import { useOpenCommentRightDrawer } from '../../comments/hooks/useOpenCommentRightDrawer'; import { usePeopleCommentsCountQuery } from '../../comments/services'; import { PersonChip } from './PersonChip'; @@ -31,6 +31,7 @@ export function EditablePeopleFullName({ }: OwnProps) { const [firstnameValue, setFirstnameValue] = useState(firstname); const [lastnameValue, setLastnameValue] = useState(lastname); + const openCommentRightDrawer = useOpenCommentRightDrawer(); function handleDoubleTextChange( firstValue: string, @@ -42,14 +43,13 @@ export function EditablePeopleFullName({ onChange(firstValue, secondValue); } - const openCommentRightDrawer = useOpenCommentRightDrawer(); - function handleCommentClick(event: React.MouseEvent) { event.preventDefault(); event.stopPropagation(); + openCommentRightDrawer([ { - type: 'Company', + type: 'Person', id: personId, }, ]); @@ -57,6 +57,8 @@ export function EditablePeopleFullName({ const commentCount = usePeopleCommentsCountQuery(personId); + const displayCommentCount = !commentCount.loading; + return ( - {commentCount.loading ? null : ( + {displayCommentCount && ( )} diff --git a/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx b/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx index 035c7f9c4..8ec488292 100644 --- a/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx +++ b/front/src/modules/ui/layout/containers/WithTopBarContainer.tsx @@ -30,10 +30,9 @@ const MainContainer = styled.div` background: ${(props) => props.theme.noisyBackground}; padding-right: ${(props) => props.theme.spacing(3)}; padding-bottom: ${(props) => props.theme.spacing(3)}; + gap: ${(props) => props.theme.spacing(2)}; `; -const RIGHT_DRAWER_WIDTH = '300px'; - type LeftContainerProps = { isRightDrawerOpen?: boolean; }; @@ -41,7 +40,11 @@ type LeftContainerProps = { const LeftContainer = styled.div` display: flex; width: calc( - 100% - ${(props) => (props.isRightDrawerOpen ? RIGHT_DRAWER_WIDTH : '0px')} + 100% - + ${(props) => + props.isRightDrawerOpen + ? `${props.theme.rightDrawerWidth} - ${props.theme.spacing(2)}` + : '0px'} ); position: relative; `; diff --git a/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx b/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx index 70b18edf1..fd8ca21bb 100644 --- a/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx +++ b/front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx @@ -12,8 +12,7 @@ import { RightDrawerRouter } from './RightDrawerRouter'; const StyledRightDrawer = styled.div` display: flex; flex-direction: row; - width: 300px; - margin-left: ${(props) => props.theme.spacing(2)}; + width: ${(props) => props.theme.rightDrawerWidth}; `; export function RightDrawer() { diff --git a/front/src/modules/ui/layout/right-drawer/components/RightDrawerBody.tsx b/front/src/modules/ui/layout/right-drawer/components/RightDrawerBody.tsx index b946f87a5..a1444d004 100644 --- a/front/src/modules/ui/layout/right-drawer/components/RightDrawerBody.tsx +++ b/front/src/modules/ui/layout/right-drawer/components/RightDrawerBody.tsx @@ -3,6 +3,4 @@ import styled from '@emotion/styled'; export const RightDrawerBody = styled.div` display: flex; flex-direction: column; - - padding: 8px; `; diff --git a/front/src/modules/ui/layout/styles/themes.ts b/front/src/modules/ui/layout/styles/themes.ts index 0a6267f64..10ae48900 100644 --- a/front/src/modules/ui/layout/styles/themes.ts +++ b/front/src/modules/ui/layout/styles/themes.ts @@ -23,6 +23,8 @@ const commonTheme = { }, borderRadius: '4px', + + rightDrawerWidth: '300px', }; const lightThemeSpecific = { @@ -38,6 +40,7 @@ const lightThemeSpecific = { purpleBackground: '#e0e0ff', yellowBackground: '#fff2e7', + primaryBackgroundTransparent: 'rgba(255, 255, 255, 0.8)', secondaryBackgroundTransparent: 'rgba(252, 252, 252, 0.8)', primaryBorder: 'rgba(0, 0, 0, 0.08)', @@ -77,6 +80,7 @@ const darkThemeSpecific: typeof lightThemeSpecific = { purpleBackground: '#1111b7', yellowBackground: '#cc660a', + primaryBackgroundTransparent: 'rgba(20, 20, 20, 0.8)', secondaryBackgroundTransparent: 'rgba(23, 23, 23, 0.8)', clickableElementBackgroundHover: 'rgba(0, 0, 0, 0.04)', diff --git a/front/src/modules/users/components/UserAvatar.tsx b/front/src/modules/users/components/UserAvatar.tsx new file mode 100644 index 000000000..6c78846f7 --- /dev/null +++ b/front/src/modules/users/components/UserAvatar.tsx @@ -0,0 +1,61 @@ +import styled from '@emotion/styled'; + +import { isNonEmptyString } from '@/utils/type-guards/isNonEmptyString'; + +type OwnProps = { + avatarUrl: string | null | undefined; + size: number; + placeholderLetter: string; +}; + +export const StyledUserAvatar = styled.div>` + width: ${(props) => props.size}px; + height: ${(props) => props.size}px; + border-radius: 50%; + background-image: url(${(props) => + isNonEmptyString(props.avatarUrl) ? props.avatarUrl : 'none'}); + background-color: ${(props) => + !isNonEmptyString(props.avatarUrl) + ? props.theme.tertiaryBackground + : 'none'}; + background-repeat: no-repeat; + background-position: center center; + background-size: cover; + + display: flex; + align-items: center; + + justify-content: center; +`; + +type StyledPlaceholderLetterProps = { + size: number; +}; + +export const StyledPlaceholderLetter = styled.div` + width: ${(props) => props.size}px; + height: ${(props) => props.size}px; + + font-size: 12px; + font-weight: 500; + + display: flex; + align-items: center; + justify-content: center; + + color: ${(props) => props.theme.text80}; +`; + +export function UserAvatar({ avatarUrl, size, placeholderLetter }: OwnProps) { + const noAvatarUrl = !isNonEmptyString(avatarUrl); + + return ( + + {noAvatarUrl && ( + + {placeholderLetter} + + )} + + ); +} diff --git a/front/src/modules/users/components/__stories__/UserAvatar.stories.tsx b/front/src/modules/users/components/__stories__/UserAvatar.stories.tsx new file mode 100644 index 000000000..7277cf8c6 --- /dev/null +++ b/front/src/modules/users/components/__stories__/UserAvatar.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { getRenderWrapperForComponent } from '~/testing/renderWrappers'; + +import { UserAvatar } from '../UserAvatar'; + +const meta: Meta = { + title: 'Components/User/UserAvatar', + component: UserAvatar, +}; + +export default meta; +type Story = StoryObj; + +const avatarUrl = + 'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4'; + +export const Size40: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const Size32: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const Size16: Story = { + render: getRenderWrapperForComponent( + , + ), +}; + +export const NoAvatarPicture: Story = { + render: getRenderWrapperForComponent( + , + ), +}; diff --git a/front/src/modules/users/interfaces/__tests__/user.interface.test.ts b/front/src/modules/users/interfaces/__tests__/user.interface.test.ts index e59efdcac..6809d466a 100644 --- a/front/src/modules/users/interfaces/__tests__/user.interface.test.ts +++ b/front/src/modules/users/interfaces/__tests__/user.interface.test.ts @@ -13,6 +13,7 @@ describe('User mappers', () => { const graphQLUser = { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', displayName: 'John Doe', + avatarUrl: 'https://example.com/avatar.png', email: 'john.doe@gmail.com', workspaceMember: { id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e88', @@ -31,6 +32,7 @@ describe('User mappers', () => { __typename: 'users', id: graphQLUser.id, displayName: graphQLUser.displayName, + avatarUrl: graphQLUser.avatarUrl, email: graphQLUser.email, workspaceMember: { id: graphQLUser.workspaceMember.id, @@ -51,6 +53,7 @@ describe('User mappers', () => { __typename: 'users', id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', displayName: 'John Doe', + avatarUrl: 'https://example.com/avatar.png', email: 'john.doe@gmail.com', workspaceMember: { id: '7af20dea-0412-4c4c-8b13-d6f0e6e09e88', @@ -65,6 +68,7 @@ describe('User mappers', () => { expect(graphQLUser).toStrictEqual({ id: user.id, displayName: user.displayName, + avatarUrl: user.avatarUrl, email: user.email, workspaceMemberId: user.workspaceMember.id, __typename: 'users', diff --git a/front/src/modules/users/interfaces/user.interface.ts b/front/src/modules/users/interfaces/user.interface.ts index ca787dd6d..ad37baa8c 100644 --- a/front/src/modules/users/interfaces/user.interface.ts +++ b/front/src/modules/users/interfaces/user.interface.ts @@ -9,6 +9,7 @@ export interface User { id: string; email?: string; displayName?: string; + avatarUrl?: string; workspaceMember?: WorkspaceMember | null; } @@ -17,6 +18,7 @@ export type GraphqlQueryUser = { email?: string; displayName?: string; workspaceMember?: GraphqlQueryWorkspaceMember | null; + avatarUrl?: string; __typename?: string; }; @@ -24,6 +26,7 @@ export type GraphqlMutationUser = { id: string; email?: string; displayName?: string; + avatarUrl?: string; workspaceMemberId?: string; __typename?: string; }; @@ -33,6 +36,7 @@ export const mapToUser = (user: GraphqlQueryUser): User => ({ id: user.id, email: user.email, displayName: user.displayName, + avatarUrl: user.avatarUrl, workspaceMember: user.workspaceMember ? mapToWorkspaceMember(user.workspaceMember) : user.workspaceMember, @@ -42,6 +46,7 @@ export const mapToGqlUser = (user: User): GraphqlMutationUser => ({ id: user.id, email: user.email, displayName: user.displayName, + avatarUrl: user.avatarUrl, workspaceMemberId: user.workspaceMember?.id, __typename: 'users', }); diff --git a/front/src/modules/utils/datetime/__tests__/date-utils.test.ts b/front/src/modules/utils/datetime/__tests__/date-utils.test.ts new file mode 100644 index 000000000..188d90928 --- /dev/null +++ b/front/src/modules/utils/datetime/__tests__/date-utils.test.ts @@ -0,0 +1,155 @@ +import { formatDistanceToNow } from 'date-fns'; +import { DateTime } from 'luxon'; + +import { logError } from '@/utils/logs/logError'; + +import { + beautifyExactDate, + beautifyPastDateAbsolute, + beautifyPastDateRelativeToNow, + DEFAULT_DATE_LOCALE, + parseDate, +} from '../date-utils'; + +jest.mock('@/utils/logs/logError'); + +describe('beautifyExactDate', () => { + it('should return the correct relative date', () => { + const mockDate = '2023-01-01T12:13:24'; + const actualDate = new Date(mockDate); + const expected = DateTime.fromJSDate(actualDate) + .toUTC() + .setLocale(DEFAULT_DATE_LOCALE) + .toFormat('DD · TT'); + + const result = beautifyExactDate(mockDate); + expect(result).toEqual(expected); + }); +}); + +describe('parseDate', () => { + it('should log an error and return empty string when passed an invalid date string', () => { + expect(() => { + parseDate('invalid-date-string'); + }).toThrow( + Error('Invalid date passed to formatPastDate: "invalid-date-string"'), + ); + }); + + it('should log an error and return empty string when passed NaN', () => { + expect(() => { + parseDate(NaN); + }).toThrow(Error('Invalid date passed to formatPastDate: "NaN"')); + }); + + it('should log an error and return empty string when passed invalid Date object', () => { + expect(() => { + parseDate(new Date(NaN)); + }).toThrow(Error('Invalid date passed to formatPastDate: "Invalid Date"')); + }); +}); + +describe('beautifyPastDateRelativeToNow', () => { + it('should return the correct relative date', () => { + const mockDate = '2023-01-01'; + const actualDate = new Date(mockDate); + const expected = formatDistanceToNow(actualDate); + + const result = beautifyPastDateRelativeToNow(mockDate); + expect(result).toEqual(expected); + }); + + it('should log an error and return empty string when passed an invalid date string', () => { + const result = beautifyPastDateRelativeToNow('invalid-date-string'); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "invalid-date-string"'), + ); + expect(result).toEqual(''); + }); + + it('should log an error and return empty string when passed NaN', () => { + const result = beautifyPastDateRelativeToNow(NaN); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "NaN"'), + ); + expect(result).toEqual(''); + }); + + it('should log an error and return empty string when passed invalid Date object', () => { + const result = beautifyPastDateRelativeToNow( + new Date('invalid-date-asdasd'), + ); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "Invalid Date"'), + ); + expect(result).toEqual(''); + }); +}); + +describe('beautifyPastDateAbsolute', () => { + it('should log an error and return empty string when passed an invalid date string', () => { + const result = beautifyPastDateAbsolute('invalid-date-string'); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "invalid-date-string"'), + ); + expect(result).toEqual(''); + }); + + it('should log an error and return empty string when passed NaN', () => { + const result = beautifyPastDateAbsolute(NaN); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "NaN"'), + ); + expect(result).toEqual(''); + }); + + it('should log an error and return empty string when passed invalid Date object', () => { + const result = beautifyPastDateAbsolute(new Date(NaN)); + + expect(logError).toHaveBeenCalledWith( + Error('Invalid date passed to formatPastDate: "Invalid Date"'), + ); + expect(result).toEqual(''); + }); + + it('should return the correct format when the date difference is less than 24 hours', () => { + const now = DateTime.local(); + const pastDate = now.minus({ hours: 23 }); + const expected = pastDate.toFormat('HH:mm'); + + const result = beautifyPastDateAbsolute(pastDate.toJSDate()); + expect(result).toEqual(expected); + }); + + it('should return the correct format when the date difference is less than 7 days', () => { + const now = DateTime.local(); + const pastDate = now.minus({ days: 6 }); + const expected = pastDate.toFormat('cccc - HH:mm'); + + const result = beautifyPastDateAbsolute(pastDate.toJSDate()); + expect(result).toEqual(expected); + }); + + it('should return the correct format when the date difference is less than 365 days', () => { + const now = DateTime.local(); + const pastDate = now.minus({ days: 364 }); + const expected = pastDate.toFormat('MMMM d - HH:mm'); + + const result = beautifyPastDateAbsolute(pastDate.toJSDate()); + expect(result).toEqual(expected); + }); + + it('should return the correct format when the date difference is more than 365 days', () => { + const now = DateTime.local(); + const pastDate = now.minus({ days: 366 }); + const expected = pastDate.toFormat('dd/MM/yyyy - HH:mm'); + + const result = beautifyPastDateAbsolute(pastDate.toJSDate()); + expect(result).toEqual(expected); + }); +}); diff --git a/front/src/modules/utils/datetime/date-utils.ts b/front/src/modules/utils/datetime/date-utils.ts new file mode 100644 index 000000000..4c1b4c03c --- /dev/null +++ b/front/src/modules/utils/datetime/date-utils.ts @@ -0,0 +1,75 @@ +import { formatDistanceToNow } from 'date-fns'; +import { DateTime } from 'luxon'; + +import { logError } from '../logs/logError'; + +export const DEFAULT_DATE_LOCALE = 'en-EN'; + +export function parseDate(dateToParse: Date | string | number) { + let formattedDate: DateTime | null = null; + + if (!dateToParse) { + throw new Error(`Invalid date passed to formatPastDate: "${dateToParse}"`); + } else if (typeof dateToParse === 'string') { + formattedDate = DateTime.fromISO(dateToParse); + } else if (dateToParse instanceof Date) { + formattedDate = DateTime.fromJSDate(dateToParse); + } else if (typeof dateToParse === 'number') { + formattedDate = DateTime.fromMillis(dateToParse); + } + + if (!formattedDate) { + throw new Error(`Invalid date passed to formatPastDate: "${dateToParse}"`); + } + + if (!formattedDate.isValid) { + throw new Error(`Invalid date passed to formatPastDate: "${dateToParse}"`); + } + + return formattedDate.setLocale(DEFAULT_DATE_LOCALE); +} + +export function beautifyExactDate(dateToBeautify: Date | string | number) { + try { + const parsedDate = parseDate(dateToBeautify); + + return parsedDate.toFormat('DD · TT'); + } catch (error) { + logError(error); + return ''; + } +} + +export function beautifyPastDateRelativeToNow( + pastDate: Date | string | number, +) { + try { + const parsedDate = parseDate(pastDate); + + return formatDistanceToNow(parsedDate.toJSDate()); + } catch (error) { + logError(error); + return ''; + } +} + +export function beautifyPastDateAbsolute(pastDate: Date | string | number) { + try { + const parsedPastDate = parseDate(pastDate); + + const hoursDiff = parsedPastDate.diffNow('hours').negate().hours; + + if (hoursDiff <= 24) { + return parsedPastDate.toFormat('HH:mm'); + } else if (hoursDiff <= 7 * 24) { + return parsedPastDate.toFormat('cccc - HH:mm'); + } else if (hoursDiff <= 365 * 24) { + return parsedPastDate.toFormat('MMMM d - HH:mm'); + } else { + return parsedPastDate.toFormat('dd/MM/yyyy - HH:mm'); + } + } catch (error) { + logError(error); + return ''; + } +} diff --git a/front/src/modules/utils/logs/logError.ts b/front/src/modules/utils/logs/logError.ts new file mode 100644 index 000000000..b00e98bb2 --- /dev/null +++ b/front/src/modules/utils/logs/logError.ts @@ -0,0 +1,3 @@ +export function logError(message: any) { + console.error(message); +} diff --git a/front/src/testing/graphqlMocks.ts b/front/src/testing/graphqlMocks.ts index 69b48e312..3b52447c6 100644 --- a/front/src/testing/graphqlMocks.ts +++ b/front/src/testing/graphqlMocks.ts @@ -5,8 +5,8 @@ import { GraphqlQueryPerson } from '@/people/interfaces/person.interface'; import { GraphqlQueryUser } from '@/users/interfaces/user.interface'; import { - GetCompanyCountsQuery, - GetPeopleCountsQuery, + GetCompanyCommentsCountQuery, + GetPeopleCommentsCountQuery, } from '../generated/graphql'; import { mockedCompaniesData } from './mock-data/companies'; @@ -98,15 +98,15 @@ export const graphqlMocks = [ }), ); }), - graphql.query('GetPeopleCounts', (req, res, ctx) => { - const mockedData: GetPeopleCountsQuery = { + graphql.query('GetPeopleCommentsCount', (req, res, ctx) => { + const mockedData: GetPeopleCommentsCountQuery = { people: [{ commentsCount: 12 }], }; return res(ctx.data(mockedData)); }), - graphql.query('GetCompanyCounts', (req, res, ctx) => { - const mockedData: GetCompanyCountsQuery = { + graphql.query('GetCompanyCommentsCount', (req, res, ctx) => { + const mockedData: GetCompanyCommentsCountQuery = { companies: [{ commentsCount: 20 }], }; return res(ctx.data(mockedData)); diff --git a/front/src/testing/mock-data/users.ts b/front/src/testing/mock-data/users.ts index 02f92db72..30cd2ca6d 100644 --- a/front/src/testing/mock-data/users.ts +++ b/front/src/testing/mock-data/users.ts @@ -6,6 +6,8 @@ export const mockedUsersData: Array = [ __typename: 'User', email: 'charles@test.com', displayName: 'Charles Test', + avatarUrl: + 'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4', workspaceMember: { __typename: 'WorkspaceMember', id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', diff --git a/server/src/database/seeds/users.ts b/server/src/database/seeds/users.ts index 44e0c5df4..ba419b877 100644 --- a/server/src/database/seeds/users.ts +++ b/server/src/database/seeds/users.ts @@ -8,6 +8,8 @@ export const seedUsers = async (prisma: PrismaClient) => { displayName: 'Charles Bochet', email: 'charles@test.com', locale: 'en', + avatarUrl: + 'https://s3-alpha-sig.figma.com/img/bbb5/4905/f0a52cc2b9aaeb0a82a360d478dae8bf?Expires=1687132800&Signature=iVBr0BADa3LHoFVGbwqO-wxC51n1o~ZyFD-w7nyTyFP4yB-Y6zFawL-igewaFf6PrlumCyMJThDLAAc-s-Cu35SBL8BjzLQ6HymzCXbrblUADMB208PnMAvc1EEUDq8TyryFjRO~GggLBk5yR0EXzZ3zenqnDEGEoQZR~TRqS~uDF-GwQB3eX~VdnuiU2iittWJkajIDmZtpN3yWtl4H630A3opQvBnVHZjXAL5YPkdh87-a-H~6FusWvvfJxfNC2ZzbrARzXofo8dUFtH7zUXGCC~eUk~hIuLbLuz024lFQOjiWq2VKyB7dQQuGFpM-OZQEV8tSfkViP8uzDLTaCg__&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4', workspaceMember: { create: { id: 'twenty-7ef9d213-1c25-4d02-bf35-6aeccf7ea419',