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

feat: default record group table #8397

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { renderHook } from '@testing-library/react';
import { ReactNode } from 'react';

import { mocks } from '@/auth/hooks/__mocks__/useAuth';
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';
import { useLoadRecordIndexTable } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';

const recordTableId = 'people';
Expand All @@ -23,12 +25,18 @@ const Wrapper = ({ children }: { children: ReactNode }) => {
return (
<HookMockWrapper>
<ObjectNamePluralSetter>
<RecordTableComponentInstance
recordTableId={recordTableId}
onColumnsChange={onColumnsChange}
<ViewComponentInstanceContext.Provider
value={{ instanceId: 'instanceId' }}
>
{children}
</RecordTableComponentInstance>
<RecordTableComponentInstance
recordTableId={recordTableId}
onColumnsChange={onColumnsChange}
>
<RecordGroupContext.Provider value={{ recordGroupId: 'default' }}>
{children}
</RecordGroupContext.Provider>
</RecordTableComponentInstance>
</ViewComponentInstanceContext.Provider>
</ObjectNamePluralSetter>
</HookMockWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups';
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';

type RecordGroupContextProviderProps = {
objectMetadataNameSingular: string;
children: React.ReactNode;
};

export const RecordGroupContextProvider = ({
objectMetadataNameSingular,
children,
}: RecordGroupContextProviderProps) => {
const { visibleRecordGroups } = useRecordGroups({
objectMetadataNameSingular,
});
magrinj marked this conversation as resolved.
Show resolved Hide resolved

if (visibleRecordGroups.length === 0) {
return (
<RecordGroupContext.Provider value={{ recordGroupId: 'default' }}>
magrinj marked this conversation as resolved.
Show resolved Hide resolved
{children}
</RecordGroupContext.Provider>
);
}

return (
<>
{visibleRecordGroups.map((recordGroup) => (
<RecordGroupContext.Provider
key={recordGroup.id}
value={{ recordGroupId: recordGroup.id }}
>
{children}
</RecordGroupContext.Provider>
))}
magrinj marked this conversation as resolved.
Show resolved Hide resolved
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState';
import { recordGroupDefaultId } from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useMemo } from 'react';

export const useCurrentRecordGroupDefinition = (recordTableId?: string) => {
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved
const currentRecordGroupId = useCurrentRecordGroupId();

const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
recordTableId,
);

const recordGroupDefinition = useMemo(() => {
if (currentRecordGroupId === recordGroupDefaultId) {
return undefined;
}

return recordGroupDefinitions.find(({ id }) => id === currentRecordGroupId);
}, [currentRecordGroupId, recordGroupDefinitions]);

return recordGroupDefinition;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext';
import { useContext } from 'react';

export const useCurrentRecordGroupId = () => {
const context = useContext(RecordGroupContext);

if (!context) {
throw new Error(
'useCurrentRecordGroup must be used within a RecordGroupContextProvider',
);
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved
}

if (!context.recordGroupId) {
throw new Error('RecordGroupContext is malformed');
}
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved

return context.recordGroupId;
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const useRecordGroupActions = () => {
});

const { viewGroupFieldMetadataItem } = useRecordGroups({
objectNameSingular,
objectMetadataNameSingular: objectNameSingular,
magrinj marked this conversation as resolved.
Show resolved Hide resolved
});

const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const useRecordGroupReorder = ({
);

const { visibleRecordGroups } = useRecordGroups({
objectNameSingular,
objectMetadataNameSingular: objectNameSingular,
});

const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { recordGroupDefinitionsComponentState } from '@/object-record/record-gro
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';

type UseRecordGroupsParams = {
objectNameSingular: string;
objectMetadataNameSingular: string;
};

export const useRecordGroups = ({
objectNameSingular,
objectMetadataNameSingular,
}: UseRecordGroupsParams) => {
const recordGroupDefinitions = useRecoilComponentValueV2(
recordGroupDefinitionsComponentState,
);

const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
objectNameSingular: objectMetadataNameSingular,
});

const viewGroupFieldMetadataItem = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createContext } from 'react';

export type RecordGroupContextProps = {
recordGroupId: string;
};

export const RecordGroupContext = createContext<RecordGroupContextProps>(
{} as RecordGroupContextProps,
);
magrinj marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const enum RecordGroupDefinitionType {
NoValue = 'no-value',
}

export const recordGroupDefaultId = 'default' as const;
magrinj marked this conversation as resolved.
Show resolved Hide resolved

export type RecordGroupDefinition = {
id: string;
fieldMetadataId: string;
Expand All @@ -15,3 +17,7 @@ export type RecordGroupDefinition = {
position: number;
isVisible: boolean;
};

export type RecordGroupDefinitionId =
| RecordGroupDefinition['id']
| typeof recordGroupDefaultId;
magrinj marked this conversation as resolved.
Show resolved Hide resolved
magrinj marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState';
Expand All @@ -14,6 +15,8 @@ import { tableViewFilterGroupsComponentState } from '@/object-record/record-tabl
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isNull } from '@sniptt/guards';
import { useMemo } from 'react';
import { isDefined } from 'twenty-ui';
import { WorkspaceActivationStatus } from '~/generated/graphql';

export const useFindManyParams = (
Expand Down Expand Up @@ -53,6 +56,8 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
objectNameSingular,
});

const currentRecordGroupDefinition = useCurrentRecordGroupDefinition();

const { setRecordTableData, setIsRecordTableInitialLoading } =
useRecordTable();
const currentWorkspace = useRecoilValue(currentWorkspaceState);
Expand All @@ -61,6 +66,31 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {

const recordGqlFields = useRecordTableRecordGqlFields({ objectMetadataItem });

const groupByFilter = useMemo(() => {
magrinj marked this conversation as resolved.
Show resolved Hide resolved
if (isDefined(currentRecordGroupDefinition)) {
const fieldMetadataItem = objectMetadataItem?.fields.find(
(fieldMetadataItem) =>
fieldMetadataItem.id === currentRecordGroupDefinition.fieldMetadataId,
);

if (!fieldMetadataItem) {
return {};
}
magrinj marked this conversation as resolved.
Show resolved Hide resolved

return {
[fieldMetadataItem.name]: {
eq: currentRecordGroupDefinition.value,
},
};
}

// TODO: Handle case when value is nullable

return {};
}, [objectMetadataItem?.fields, currentRecordGroupDefinition]);

// TODO: Don't fetch records based on visibleRecordGroups here, this hook instead should be placed somewhere else with a dedicated filter for a given visible record group

const {
records,
loading,
Expand All @@ -70,6 +100,10 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
hasNextPage,
} = useFindManyRecords({
...params,
filter: {
...params.filter,
...groupByFilter,
},
magrinj marked this conversation as resolved.
Show resolved Hide resolved
recordGqlFields,
onCompleted: () => {
setIsRecordTableInitialLoading(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const RecordIndexOptionsDropdownContent = ({
visibleRecordGroups,
viewGroupFieldMetadataItem,
} = useRecordGroups({
objectNameSingular: objectMetadataItem.nameSingular,
objectMetadataNameSingular: objectMetadataItem.nameSingular,
});
const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { isNonEmptyString, isNull } from '@sniptt/guards';

import { RecordGroupContextProvider } from '@/object-record/record-group/components/RecordGroupContextProvider';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
Expand All @@ -12,7 +13,7 @@ import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/reco
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
Expand Down Expand Up @@ -40,17 +41,17 @@ export const RecordTable = ({
}: RecordTableProps) => {
const tableBodyRef = useRef<HTMLTableElement>(null);

const { toggleClickOutsideListener } = useClickOutsideListener(
RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID,
);

const isRecordTableInitialLoading = useRecoilComponentValueV2(
isRecordTableInitialLoadingComponentState,
recordTableId,
);

const { toggleClickOutsideListener } = useClickOutsideListener(
RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID,
);

const tableRowIds = useRecoilComponentValueV2(
tableRowIdsComponentState,
tableAllRowIdsComponentState,
recordTableId,
);

Expand All @@ -59,15 +60,15 @@ export const RecordTable = ({
recordTableId,
);

const { resetTableRowSelection, setRowSelected } = useRecordTable({
recordTableId,
});

const recordTableIsEmpty =
!isRecordTableInitialLoading &&
tableRowIds.length === 0 &&
isNull(pendingRecordId);

const { resetTableRowSelection, setRowSelected } = useRecordTable({
recordTableId,
});

if (!isNonEmptyString(objectNameSingular)) {
return <></>;
}
Expand All @@ -82,7 +83,11 @@ export const RecordTable = ({
recordTableId={recordTableId}
viewBarId={viewBarId}
>
<RecordTableBodyEffect />
<RecordGroupContextProvider
magrinj marked this conversation as resolved.
Show resolved Hide resolved
objectMetadataNameSingular={objectNameSingular}
>
<RecordTableBodyEffect />
</RecordGroupContextProvider>
magrinj marked this conversation as resolved.
Show resolved Hide resolved
<RecordTableBodyUnselectEffect
tableBodyRef={tableBodyRef}
recordTableId={recordTableId}
Expand All @@ -95,7 +100,9 @@ export const RecordTable = ({
<RecordTableHeader
objectMetadataNameSingular={objectNameSingular}
/>
<RecordTableBody />
<RecordTableBody
objectMetadataNameSingular={objectNameSingular}
/>
</StyledTable>
<DragSelect
dragSelectable={tableBodyRef}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
import { recordGroupDefaultId } from '@/object-record/record-group/types/RecordGroupDefinition';
import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader';
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';

export const RecordTableRows = () => {
const tableRowIds = useRecoilComponentValueV2(tableRowIdsComponentState);
const recordGroupId = useCurrentRecordGroupId();

const tableRowIdsByGroup = useRecoilComponentFamilyValueV2(
tableRowIdsByGroupComponentFamilyState,
recordGroupId,
);

return (
<>
{tableRowIds.map((recordId, rowIndex) => {
{tableRowIdsByGroup.map((recordId, rowIndex) => {
return (
<RecordTableRow
key={recordId}
Expand All @@ -17,7 +24,9 @@ export const RecordTableRows = () => {
/>
);
})}
<RecordTableBodyFetchMoreLoader />
{recordGroupId !== recordGroupDefaultId && (
<RecordTableBodyFetchMoreLoader />
)}
magrinj marked this conversation as resolved.
Show resolved Hide resolved
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const meta: Meta = {
value={{
columnDefinition: mockPerformance.fieldDefinition,
columnIndex: 0,
cellPosition: { row: 0, column: 0 },
cellPosition: { recordId: 'recordId', column: 0 },
hasSoftFocus: false,
isInEditMode: false,
}}
Expand Down
Loading
Loading