Skip to content

Commit

Permalink
feat: Revamp navigation bar (#6031)
Browse files Browse the repository at this point in the history
closes: #4428

Testing for fetchMoreRecords is pending, along with component tests

---------

Co-authored-by: Lucas Bordeau <[email protected]>
  • Loading branch information
AdityaPimpalkar and lucasbordeau authored Jul 16, 2024
1 parent a8dfff3 commit 4a67cfa
Show file tree
Hide file tree
Showing 66 changed files with 1,056 additions and 365 deletions.
20 changes: 20 additions & 0 deletions packages/twenty-front/src/hooks/useScrollToPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { overlayScrollbarsState } from '@/ui/utilities/scroll/states/overlayScrollbarsState';
import { useRecoilCallback } from 'recoil';

export const useScrollToPosition = () => {
const scrollToPosition = useRecoilCallback(
({ snapshot }) =>
(scrollPositionInPx: number) => {
const overlayScrollbars = snapshot
.getLoadable(overlayScrollbarsState)
.getValue();

const scrollWrapper = overlayScrollbars?.elements().viewport;

scrollWrapper?.scrollTo({ top: scrollPositionInPx });
},
[],
);

return { scrollToPosition };
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext } from 'react';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { format } from 'date-fns';
import { useContext } from 'react';
import { useRecoilValue } from 'recoil';
import { Avatar, AvatarGroup, IconArrowRight, IconLock } from 'twenty-ui';

Expand All @@ -14,8 +14,8 @@ import { hasCalendarEventEnded } from '@/activities/calendar/utils/hasCalendarEv
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Card } from '@/ui/layout/card/components/Card';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { CalendarChannelVisibility } from '~/generated/graphql';
import { TimelineCalendarEvent } from '~/generated-metadata/graphql';
import { CalendarChannelVisibility } from '~/generated/graphql';
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
import { isDefined } from '~/utils/isDefined';

Expand Down Expand Up @@ -169,7 +169,9 @@ export const CalendarEventRow = ({
? `${participant.firstName} ${participant.lastName}`
: participant.displayName
}
entityId={participant.workspaceMemberId ?? participant.personId}
placeholderColorSeed={
participant.workspaceMemberId ?? participant.personId
}
type="rounded"
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
<Avatar
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl)}
size="md"
entityId={author?.id}
placeholderColorSeed={author?.id}
placeholder={authorName}
/>
<StyledName>{authorName}</StyledName>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useMemo, useRef } from 'react';
import styled from '@emotion/styled';
import { isNonEmptyString } from '@sniptt/guards';
import { useMemo, useRef } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { Avatar, IconNotes, IconSparkles } from 'twenty-ui';
Expand Down Expand Up @@ -377,7 +377,7 @@ export const CommandMenu = () => {
<Avatar
type="rounded"
avatarUrl={null}
entityId={person.id}
placeholderColorSeed={person.id}
placeholder={
person.name.firstName +
' ' +
Expand All @@ -399,7 +399,7 @@ export const CommandMenu = () => {
to={`object/company/${company.id}`}
Icon={() => (
<Avatar
entityId={company.id}
placeholderColorSeed={company.id}
placeholder={company.name}
avatarUrl={getLogoUrlFromDomainName(
company.domainName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const Favorites = () => {
label={labelIdentifier}
Icon={() => (
<StyledAvatar
entityId={recordId}
placeholderColorSeed={recordId}
avatarUrl={getImageAbsoluteURIOrBase64(avatarUrl)}
type={avatarType}
placeholder={labelIdentifier}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { useMemo } from 'react';
import React from 'react';
import { useRecoilValue } from 'recoil';

import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
import { getRecordChipGeneratorPerObjectPerField } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField';
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';

export const ObjectMetadataItemsProvider = ({
Expand All @@ -15,23 +14,15 @@ export const ObjectMetadataItemsProvider = ({

const shouldDisplayChildren = objectMetadataItems.length > 0;

const chipGeneratorPerObjectPerField = useMemo(() => {
return getRecordChipGeneratorPerObjectPerField(objectMetadataItems);
}, [objectMetadataItems]);

return (
<>
<ObjectMetadataItemsLoadEffect />
{shouldDisplayChildren ? (
<PreComputedChipGeneratorsContext.Provider
value={{
chipGeneratorPerObjectPerField,
}}
>
<PreComputedChipGeneratorsProvider>
<RelationPickerScope relationPickerScopeId="relation-picker">
{children}
</RelationPickerScope>
</PreComputedChipGeneratorsContext.Provider>
</PreComputedChipGeneratorsProvider>
) : (
<UserOrMetadataLoader />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useMemo } from 'react';
import { useRecoilValue } from 'recoil';

import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getRecordChipGenerators } from '@/object-record/utils/getRecordChipGenerators';

export const PreComputedChipGeneratorsProvider = ({
children,
}: React.PropsWithChildren) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);

const { chipGeneratorPerObjectPerField, identifierChipGeneratorPerObject } =
useMemo(() => {
return getRecordChipGenerators(objectMetadataItems);
}, [objectMetadataItems]);

return (
<>
<PreComputedChipGeneratorsContext.Provider
value={{
chipGeneratorPerObjectPerField,
identifierChipGeneratorPerObject,
}}
>
{children}
</PreComputedChipGeneratorsContext.Provider>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ import { createContext } from 'react';
import { RecordChipData } from '@/object-record/record-field/types/RecordChipData';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';

export type ChipGeneratorPerObjectPerField = Record<
export type ChipGeneratorPerObjectNameSingularPerFieldName = Record<
string,
Record<string, (record: ObjectRecord) => RecordChipData>
>;

export type IdentifierChipGeneratorPerObject = Record<
string,
(record: ObjectRecord) => RecordChipData
>;

export type PreComputedChipGeneratorsContextProps = {
chipGeneratorPerObjectPerField: ChipGeneratorPerObjectPerField;
chipGeneratorPerObjectPerField: ChipGeneratorPerObjectNameSingularPerFieldName;
identifierChipGeneratorPerObject: IdentifierChipGeneratorPerObject;
};

export const PreComputedChipGeneratorsContext =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const getLabelIdentifierFieldValue = (
record: ObjectRecord,
labelIdentifierFieldMetadataItem: FieldMetadataItem | undefined,
objectNameSingular: string,
) => {
): string => {
if (
objectNameSingular === CoreObjectNameSingular.WorkspaceMember ||
labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName
Expand All @@ -17,7 +17,7 @@ export const getLabelIdentifierFieldValue = (
}

if (isDefined(labelIdentifierFieldMetadataItem?.name)) {
return record[labelIdentifierFieldMetadataItem.name] as string | number;
return String(record[labelIdentifierFieldMetadataItem.name]);
}

return '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';

export const getLinkToShowPage = (
objectNameSingular: string,
record: ObjectRecord,
record: Pick<ObjectRecord, 'id'>,
) => {
const basePathToShowPage = getBasePathToShowPage({
objectNameSingular,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { EntityChip, EntityChipVariant } from 'twenty-ui';
import { AvatarChip, AvatarChipVariant } from 'twenty-ui';

import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
import { useRecordChipData } from '@/object-record/hooks/useRecordChipData';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isNonEmptyString } from '@sniptt/guards';
import { MouseEvent } from 'react';
import { useNavigate } from 'react-router-dom';

export type RecordChipProps = {
objectNameSingular: string;
record: ObjectRecord;
className?: string;
variant?: EntityChipVariant;
variant?: AvatarChipVariant;
};

export const RecordChip = ({
Expand All @@ -16,19 +20,29 @@ export const RecordChip = ({
className,
variant,
}: RecordChipProps) => {
const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({
const navigate = useNavigate();

const { recordChipData } = useRecordChipData({
objectNameSingular,
record,
});

const objectRecordIdentifier = mapToObjectRecordIdentifier(record);
const handleAvatarChipClick = (event: MouseEvent) => {
const linkToShowPage = getLinkToShowPage(objectNameSingular, record);

if (isNonEmptyString(linkToShowPage)) {
event.stopPropagation();
navigate(linkToShowPage);
}
};

return (
<EntityChip
entityId={record.id}
name={objectRecordIdentifier.name}
avatarType={objectRecordIdentifier.avatarType}
avatarUrl={objectRecordIdentifier.avatarUrl ?? ''}
linkToEntity={objectRecordIdentifier.linkToShowPage}
<AvatarChip
placeholderColorSeed={record.id}
name={recordChipData.name}
avatarType={recordChipData.avatarType}
avatarUrl={recordChipData.avatarUrl ?? ''}
onClick={handleAvatarChipClick}
className={className}
variant={variant}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
import { QueryCursorDirection } from '@/object-record/utils/generateFindManyRecordsQuery';

export type RecordGqlOperationVariables = {
filter?: RecordGqlOperationFilter;
orderBy?: RecordGqlOperationOrderBy;
limit?: number;
cursorFilter?: {
cursor: string;
cursorDirection: QueryCursorDirection;
limit: number;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const useFetchAllRecordIds = <T>({
const firstQueryResult =
findManyRecordsDataResult?.data?.[objectMetadataItem.namePlural];

const totalCount = firstQueryResult?.totalCount ?? 1;
const totalCount = firstQueryResult?.totalCount ?? 0;

const recordsCount = firstQueryResult?.edges.length ?? 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
fetchPolicy,
onError,
onCompleted,
cursorFilter,
}: UseFindManyRecordsParams<T>) => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { objectMetadataItem } = useObjectMetadataItem({
Expand All @@ -42,6 +43,7 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
const { findManyRecordsQuery } = useFindManyRecordsQuery({
objectNameSingular,
recordGqlFields,
cursorDirection: cursorFilter?.cursorDirection,
});

const { handleFindManyRecordsError } = useHandleFindManyRecordsError({
Expand All @@ -67,15 +69,16 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
skip: skip || !objectMetadataItem || !currentWorkspaceMember,
variables: {
filter,
limit,
orderBy,
lastCursor: cursorFilter?.cursor ?? undefined,
limit: cursorFilter?.limit ?? limit,
},
fetchPolicy: fetchPolicy,
onCompleted: handleFindManyRecordsCompleted,
onError: handleFindManyRecordsError,
});

const { fetchMoreRecords, totalCount, records, hasNextPage } =
const { fetchMoreRecords, records, hasNextPage } =
useFetchMoreRecordsWithPagination<T>({
objectNameSingular,
filter,
Expand All @@ -87,6 +90,9 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
objectMetadataItem,
});

const pageInfo = data?.[objectMetadataItem.namePlural].pageInfo;
const totalCount = data?.[objectMetadataItem.namePlural].totalCount;

return {
objectMetadataItem,
records,
Expand All @@ -96,5 +102,6 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
fetchMoreRecords,
queryStateIdentifier: queryIdentifier,
hasNextPage,
pageInfo,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ import { useRecoilValue } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
import {
generateFindManyRecordsQuery,
QueryCursorDirection,
} from '@/object-record/utils/generateFindManyRecordsQuery';

export const useFindManyRecordsQuery = ({
objectNameSingular,
recordGqlFields,
computeReferences,
cursorDirection = 'after',
}: {
objectNameSingular: string;
recordGqlFields?: RecordGqlOperationGqlRecordFields;
computeReferences?: boolean;
cursorDirection?: QueryCursorDirection;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
Expand All @@ -25,6 +30,7 @@ export const useFindManyRecordsQuery = ({
objectMetadataItems,
recordGqlFields,
computeReferences,
cursorDirection,
});

return {
Expand Down
Loading

0 comments on commit 4a67cfa

Please sign in to comment.