New view picker (#4610)
* Implement new view picker * Complete feature * Fixes according to review
This commit is contained in:
@ -8,6 +8,7 @@ import { onRecordBoardFetchMoreVisibilityChangeComponentState } from '@/object-r
|
||||
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
|
||||
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
||||
import { recordBoardFiltersComponentState } from '@/object-record/record-board/states/recordBoardFiltersComponentState';
|
||||
import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState';
|
||||
import { recordBoardObjectSingularNameComponentState } from '@/object-record/record-board/states/recordBoardObjectSingularNameComponentState';
|
||||
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState';
|
||||
import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState';
|
||||
@ -32,6 +33,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => {
|
||||
recordBoardObjectSingularNameComponentState,
|
||||
scopeId,
|
||||
),
|
||||
kanbanFieldMetadataNameState: extractComponentState(
|
||||
recordBoardKanbanFieldMetadataNameComponentState,
|
||||
scopeId,
|
||||
),
|
||||
isFetchingRecordState: extractComponentState(
|
||||
isRecordBoardFetchingRecordsComponentState,
|
||||
scopeId,
|
||||
|
||||
@ -10,6 +10,7 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
||||
recordIdsByColumnIdFamilyState,
|
||||
columnsFamilySelector,
|
||||
columnIdsState,
|
||||
kanbanFieldMetadataNameState,
|
||||
} = useRecordBoardStates(recordBoardId);
|
||||
|
||||
const setRecordIds = useRecoilCallback(
|
||||
@ -26,8 +27,18 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
||||
.getLoadable(recordIdsByColumnIdFamilyState(columnId))
|
||||
.getValue();
|
||||
|
||||
const kanbanFieldMetadataName = snapshot
|
||||
.getLoadable(kanbanFieldMetadataNameState)
|
||||
.getValue();
|
||||
|
||||
if (!kanbanFieldMetadataName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const columnRecordIds = records
|
||||
.filter((record) => record.stage === column?.value)
|
||||
.filter(
|
||||
(record) => record[kanbanFieldMetadataName] === column?.value,
|
||||
)
|
||||
.sort(sortRecordsByPosition)
|
||||
.map((record) => record.id);
|
||||
|
||||
@ -36,7 +47,12 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
||||
}
|
||||
});
|
||||
},
|
||||
[columnsFamilySelector, columnIdsState, recordIdsByColumnIdFamilyState],
|
||||
[
|
||||
columnIdsState,
|
||||
columnsFamilySelector,
|
||||
recordIdsByColumnIdFamilyState,
|
||||
kanbanFieldMetadataNameState,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -12,12 +12,16 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
||||
selectedRecordIdsSelector,
|
||||
isCompactModeActiveState,
|
||||
onFetchMoreVisibilityChangeState,
|
||||
kanbanFieldMetadataNameState,
|
||||
} = useRecordBoardStates(recordBoardId);
|
||||
|
||||
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
|
||||
const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId);
|
||||
const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState);
|
||||
const setObjectSingularName = useSetRecoilState(objectSingularNameState);
|
||||
const setKanbanFieldMetadataName = useSetRecoilState(
|
||||
kanbanFieldMetadataNameState,
|
||||
);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
@ -25,6 +29,7 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
||||
setRecordIds,
|
||||
setFieldDefinitions,
|
||||
setObjectSingularName,
|
||||
setKanbanFieldMetadataName,
|
||||
selectedRecordIdsSelector,
|
||||
isCompactModeActiveState,
|
||||
onFetchMoreVisibilityChangeState,
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const recordBoardKanbanFieldMetadataNameComponentState =
|
||||
createComponentState<string | undefined>({
|
||||
key: 'recordBoardKanbanFieldMetadataNameComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -8,7 +8,10 @@ import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoar
|
||||
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
||||
import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||
import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type RecordIndexBoardContainerEffectProps = {
|
||||
objectNameSingular: string;
|
||||
@ -31,6 +34,7 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
selectedRecordIdsSelector,
|
||||
setFieldDefinitions,
|
||||
onFetchMoreVisibilityChangeState,
|
||||
setKanbanFieldMetadataName,
|
||||
} = useRecordBoard(recordBoardId);
|
||||
|
||||
const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({
|
||||
@ -43,6 +47,10 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
onFetchMoreVisibilityChangeState,
|
||||
);
|
||||
|
||||
const recordIndexKanbanFieldMetadataId = useRecoilValue(
|
||||
recordIndexKanbanFieldMetadataIdState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setOnFetchMoreVisibilityChange(() => () => {
|
||||
if (!loading) {
|
||||
@ -67,6 +75,7 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
setColumns(
|
||||
computeRecordBoardColumnDefinitionsFromObjectMetadata(
|
||||
objectMetadataItem,
|
||||
recordIndexKanbanFieldMetadataId ?? '',
|
||||
navigateToSelectSettings,
|
||||
),
|
||||
);
|
||||
@ -74,6 +83,7 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
navigateToSelectSettings,
|
||||
objectMetadataItem,
|
||||
objectNameSingular,
|
||||
recordIndexKanbanFieldMetadataId,
|
||||
setColumns,
|
||||
]);
|
||||
|
||||
@ -85,6 +95,24 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
setFieldDefinitions(recordIndexFieldDefinitions);
|
||||
}, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(recordIndexKanbanFieldMetadataId)) {
|
||||
const kanbanFieldMetadataName = objectMetadataItem?.fields.find(
|
||||
(field) =>
|
||||
field.type === FieldMetadataType.Select &&
|
||||
field.id === recordIndexKanbanFieldMetadataId,
|
||||
)?.name;
|
||||
|
||||
if (isDefined(kanbanFieldMetadataName)) {
|
||||
setKanbanFieldMetadataName(kanbanFieldMetadataName);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
objectMetadataItem,
|
||||
recordIndexKanbanFieldMetadataId,
|
||||
setKanbanFieldMetadataName,
|
||||
]);
|
||||
|
||||
const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector());
|
||||
|
||||
const { setActionBarEntries, setContextMenuEntries } = useRecordActionBar({
|
||||
|
||||
@ -10,10 +10,10 @@ import { RecordIndexTableContainer } from '@/object-record/record-index/componen
|
||||
import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect';
|
||||
import { RecordIndexViewBarEffect } from '@/object-record/record-index/components/RecordIndexViewBarEffect';
|
||||
import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options/components/RecordIndexOptionsDropdown';
|
||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
||||
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
|
||||
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
|
||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
@ -65,6 +65,9 @@ export const RecordIndexContainer = ({
|
||||
const setRecordIndexIsCompactModeActive = useSetRecoilState(
|
||||
recordIndexIsCompactModeActiveState,
|
||||
);
|
||||
const setRecordIndexViewKanbanFieldMetadataIdState = useSetRecoilState(
|
||||
recordIndexKanbanFieldMetadataIdState,
|
||||
);
|
||||
|
||||
const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({
|
||||
recordTableId: recordIndexId,
|
||||
@ -129,9 +132,11 @@ export const RecordIndexContainer = ({
|
||||
mapViewSortsToSorts(view.viewSorts, sortDefinitions),
|
||||
);
|
||||
setRecordIndexViewType(view.type);
|
||||
setRecordIndexViewKanbanFieldMetadataIdState(
|
||||
view.kanbanFieldMetadataId,
|
||||
);
|
||||
setRecordIndexIsCompactModeActive(view.isCompact);
|
||||
}}
|
||||
optionsDropdownScopeId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
||||
/>
|
||||
<RecordIndexViewBarEffect
|
||||
objectNamePlural={objectNamePlural}
|
||||
|
||||
@ -3,7 +3,6 @@ import { RecordIndexOptionsDropdownContent } from '@/object-record/record-index/
|
||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useViewBarEditMode } from '@/views/hooks/useViewBarEditMode';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
type RecordIndexOptionsDropdownProps = {
|
||||
@ -17,8 +16,6 @@ export const RecordIndexOptionsDropdown = ({
|
||||
objectNameSingular,
|
||||
viewType,
|
||||
}: RecordIndexOptionsDropdownProps) => {
|
||||
const { setViewEditMode } = useViewBarEditMode(recordIndexId);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
||||
@ -32,7 +29,6 @@ export const RecordIndexOptionsDropdown = ({
|
||||
recordIndexId={recordIndexId}
|
||||
/>
|
||||
}
|
||||
onClickOutside={() => setViewEditMode('none')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
||||
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
|
||||
@ -14,7 +13,6 @@ import {
|
||||
IconTag,
|
||||
} from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
@ -23,8 +21,6 @@ import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemTog
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { useHandleViews } from '@/views/hooks/useHandleViews';
|
||||
import { useViewBarEditMode } from '@/views/hooks/useViewBarEditMode';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
type RecordIndexOptionsMenu = 'fields';
|
||||
@ -40,9 +36,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
recordIndexId,
|
||||
objectNameSingular,
|
||||
}: RecordIndexOptionsDropdownContentProps) => {
|
||||
const { updateCurrentView, createEmptyView, selectView } =
|
||||
useHandleViews(recordIndexId);
|
||||
const { viewEditMode, setViewEditMode } = useViewBarEditMode(recordIndexId);
|
||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||
|
||||
const { closeDropdown } = useDropdown(RECORD_INDEX_OPTIONS_DROPDOWN_ID);
|
||||
@ -53,8 +46,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleSelectMenu = (option: RecordIndexOptionsMenu) => {
|
||||
setCurrentMenu(option);
|
||||
};
|
||||
@ -67,25 +58,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
TableOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
async () => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
if (viewEditMode === 'create') {
|
||||
const id = v4();
|
||||
await createEmptyView(id, name ?? '');
|
||||
selectView(id);
|
||||
} else {
|
||||
updateCurrentView({ name });
|
||||
}
|
||||
|
||||
resetMenu();
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
},
|
||||
TableOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
const {
|
||||
handleColumnVisibilityChange,
|
||||
handleReorderColumns,
|
||||
@ -128,37 +100,18 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
return (
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode === 'create'
|
||||
? ''
|
||||
: currentViewWithCombinedFiltersAndSorts?.name
|
||||
}
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => handleSelectMenu('fields')}
|
||||
LeftIcon={IconTag}
|
||||
text="Fields"
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => handleSelectMenu('fields')}
|
||||
LeftIcon={IconTag}
|
||||
text="Fields"
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => openRecordSpreadsheetImport()}
|
||||
LeftIcon={IconFileImport}
|
||||
text="Import"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
<MenuItem
|
||||
onClick={() => openRecordSpreadsheetImport()}
|
||||
LeftIcon={IconFileImport}
|
||||
text="Import"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
{currentMenu === 'fields' && (
|
||||
<>
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||
|
||||
export const recordIndexKanbanFieldMetadataIdState = createState<string | null>(
|
||||
{
|
||||
key: 'recordIndexKanbanFieldMetadataIdState',
|
||||
defaultValue: null,
|
||||
},
|
||||
);
|
||||
@ -5,10 +5,13 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const computeRecordBoardColumnDefinitionsFromObjectMetadata = (
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
kanbanFieldMetadataId: string,
|
||||
navigateToSelectSettings: () => void,
|
||||
): RecordBoardColumnDefinition[] => {
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.type === FieldMetadataType.Select,
|
||||
(field) =>
|
||||
field.id === kanbanFieldMetadataId &&
|
||||
field.type === FieldMetadataType.Select,
|
||||
);
|
||||
|
||||
if (!selectFieldMetadataItem) {
|
||||
|
||||
Reference in New Issue
Block a user