feat: create view from current table columns + persist view fields on… (#1308)
feat: create view from current table columns + persist view fields on Update View button click Closes #1302, Closes #1307
This commit is contained in:
@ -1,5 +1,3 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { companyViewFields } from '@/companies/constants/companyViewFields';
|
import { companyViewFields } from '@/companies/constants/companyViewFields';
|
||||||
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
||||||
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
||||||
@ -11,10 +9,7 @@ import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTable
|
|||||||
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
|
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||||
import { useViewFilters } from '@/views/hooks/useViewFilters';
|
|
||||||
import { useViews } from '@/views/hooks/useViews';
|
|
||||||
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
|
||||||
import {
|
import {
|
||||||
SortOrder,
|
SortOrder,
|
||||||
UpdateOneCompanyMutationVariables,
|
UpdateOneCompanyMutationVariables,
|
||||||
@ -37,31 +32,18 @@ export function CompanyTable() {
|
|||||||
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||||
|
|
||||||
const objectId = 'company';
|
const { handleViewsChange, handleViewSubmit } = useTableViews({
|
||||||
const { handleViewsChange } = useViews({
|
|
||||||
availableFilters: companiesFilters,
|
availableFilters: companiesFilters,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
objectId,
|
objectId: 'company',
|
||||||
});
|
|
||||||
const { handleColumnsChange } = useTableViewFields({
|
|
||||||
objectName: objectId,
|
|
||||||
viewFieldDefinitions: companyViewFields,
|
viewFieldDefinitions: companyViewFields,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { persistFilters } = useViewFilters({
|
|
||||||
availableFilters: companiesFilters,
|
|
||||||
});
|
|
||||||
const { persistSorts } = useViewSorts({ availableSorts });
|
|
||||||
const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
|
const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
|
||||||
|
|
||||||
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
||||||
const { setActionBarEntries } = useCompanyTableActionBarEntries();
|
const { setActionBarEntries } = useCompanyTableActionBarEntries();
|
||||||
|
|
||||||
const handleViewSubmit = useCallback(async () => {
|
|
||||||
await persistFilters();
|
|
||||||
await persistSorts();
|
|
||||||
}, [persistFilters, persistSorts]);
|
|
||||||
|
|
||||||
function handleImport() {
|
function handleImport() {
|
||||||
openCompanySpreadsheetImport();
|
openCompanySpreadsheetImport();
|
||||||
}
|
}
|
||||||
@ -80,7 +62,6 @@ export function CompanyTable() {
|
|||||||
<EntityTable
|
<EntityTable
|
||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
onColumnsChange={handleColumnsChange}
|
|
||||||
onViewsChange={handleViewsChange}
|
onViewsChange={handleViewsChange}
|
||||||
onViewSubmit={handleViewSubmit}
|
onViewSubmit={handleViewSubmit}
|
||||||
onImport={handleImport}
|
onImport={handleImport}
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
||||||
import { tableColumnsState } from '@/ui/table/states/tableColumnsState';
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
import { companyViewFields } from '../../constants/companyViewFields';
|
import { companyViewFields } from '../../constants/companyViewFields';
|
||||||
|
|
||||||
import { mockedCompaniesData } from './companies-mock-data';
|
import { mockedCompaniesData } from './companies-mock-data';
|
||||||
|
|
||||||
export function CompanyTableMockData() {
|
export function CompanyTableMockData() {
|
||||||
const setColumns = useSetRecoilState(tableColumnsState);
|
const [, setColumns] = useRecoilScopedState(
|
||||||
|
tableColumnsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
const setEntityTableData = useSetEntityTableData();
|
const setEntityTableData = useSetEntityTableData();
|
||||||
|
|
||||||
setEntityTableData(mockedCompaniesData, []);
|
setEntityTableData(mockedCompaniesData, []);
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { peopleViewFields } from '@/people/constants/peopleViewFields';
|
import { peopleViewFields } from '@/people/constants/peopleViewFields';
|
||||||
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
|
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
|
||||||
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
|
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
|
||||||
@ -11,10 +9,7 @@ import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTable
|
|||||||
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
|
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||||
import { useViewFilters } from '@/views/hooks/useViewFilters';
|
|
||||||
import { useViews } from '@/views/hooks/useViews';
|
|
||||||
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
|
||||||
import {
|
import {
|
||||||
SortOrder,
|
SortOrder,
|
||||||
UpdateOnePersonMutationVariables,
|
UpdateOnePersonMutationVariables,
|
||||||
@ -38,29 +33,16 @@ export function PeopleTable() {
|
|||||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||||
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
|
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
|
||||||
|
|
||||||
const objectId = 'person';
|
const { handleViewsChange, handleViewSubmit } = useTableViews({
|
||||||
const { handleViewsChange } = useViews({
|
|
||||||
availableFilters: peopleFilters,
|
availableFilters: peopleFilters,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
objectId,
|
objectId: 'person',
|
||||||
});
|
|
||||||
const { handleColumnsChange } = useTableViewFields({
|
|
||||||
objectName: objectId,
|
|
||||||
viewFieldDefinitions: peopleViewFields,
|
viewFieldDefinitions: peopleViewFields,
|
||||||
});
|
});
|
||||||
const { persistFilters } = useViewFilters({
|
|
||||||
availableFilters: peopleFilters,
|
|
||||||
});
|
|
||||||
const { persistSorts } = useViewSorts({ availableSorts });
|
|
||||||
|
|
||||||
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
||||||
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
||||||
|
|
||||||
const handleViewSubmit = useCallback(async () => {
|
|
||||||
await persistFilters();
|
|
||||||
await persistSorts();
|
|
||||||
}, [persistFilters, persistSorts]);
|
|
||||||
|
|
||||||
function handleImport() {
|
function handleImport() {
|
||||||
openPersonSpreadsheetImport();
|
openPersonSpreadsheetImport();
|
||||||
}
|
}
|
||||||
@ -79,7 +61,6 @@ export function PeopleTable() {
|
|||||||
<EntityTable
|
<EntityTable
|
||||||
viewName="All People"
|
viewName="All People"
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
onColumnsChange={handleColumnsChange}
|
|
||||||
onViewsChange={handleViewsChange}
|
onViewsChange={handleViewsChange}
|
||||||
onViewSubmit={handleViewSubmit}
|
onViewSubmit={handleViewSubmit}
|
||||||
onImport={handleImport}
|
onImport={handleImport}
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { SortType } from '@/ui/filter-n-sort/types/interface';
|
import { SortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
@ -92,7 +88,6 @@ type OwnProps<SortField> = {
|
|||||||
viewName: string;
|
viewName: string;
|
||||||
viewIcon?: React.ReactNode;
|
viewIcon?: React.ReactNode;
|
||||||
availableSorts?: Array<SortType<SortField>>;
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
onViewSubmit?: () => void;
|
onViewSubmit?: () => void;
|
||||||
onImport?: () => void;
|
onImport?: () => void;
|
||||||
@ -102,7 +97,6 @@ type OwnProps<SortField> = {
|
|||||||
export function EntityTable<SortField>({
|
export function EntityTable<SortField>({
|
||||||
viewName,
|
viewName,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
onColumnsChange,
|
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
onViewSubmit,
|
onViewSubmit,
|
||||||
onImport,
|
onImport,
|
||||||
@ -131,7 +125,6 @@ export function EntityTable<SortField>({
|
|||||||
<TableHeader
|
<TableHeader
|
||||||
viewName={viewName}
|
viewName={viewName}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
onColumnsChange={onColumnsChange}
|
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
onViewSubmit={onViewSubmit}
|
onViewSubmit={onViewSubmit}
|
||||||
onImport={onImport}
|
onImport={onImport}
|
||||||
@ -139,7 +132,7 @@ export function EntityTable<SortField>({
|
|||||||
<ScrollWrapper>
|
<ScrollWrapper>
|
||||||
<div>
|
<div>
|
||||||
<StyledTable>
|
<StyledTable>
|
||||||
<EntityTableHeader onColumnsChange={onColumnsChange} />
|
<EntityTableHeader />
|
||||||
<EntityTableBody />
|
<EntityTableBody />
|
||||||
</StyledTable>
|
</StyledTable>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { cloneElement, ComponentProps, useRef } from 'react';
|
import { cloneElement, ComponentProps, useRef } from 'react';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
|
||||||
import { IconButton } from '@/ui/button/components/IconButton';
|
import { IconButton } from '@/ui/button/components/IconButton';
|
||||||
import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem';
|
import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem';
|
||||||
@ -9,8 +8,10 @@ import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu'
|
|||||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||||
import { IconPlus } from '@/ui/icon';
|
import { IconPlus } from '@/ui/icon';
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
import { hiddenTableColumnsState } from '../states/tableColumnsState';
|
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
|
||||||
|
|
||||||
const StyledColumnMenu = styled(StyledDropdownMenu)`
|
const StyledColumnMenu = styled(StyledDropdownMenu)`
|
||||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||||
@ -29,7 +30,10 @@ export const EntityTableColumnMenu = ({
|
|||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
const hiddenColumns = useRecoilScopedValue(
|
||||||
|
hiddenTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
useListenClickOutside({
|
useListenClickOutside({
|
||||||
refs: [ref],
|
refs: [ref],
|
||||||
|
|||||||
@ -1,23 +1,20 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { IconButton } from '@/ui/button/components/IconButton';
|
import { IconButton } from '@/ui/button/components/IconButton';
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { IconPlus } from '@/ui/icon';
|
import { IconPlus } from '@/ui/icon';
|
||||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
|
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import { resizeFieldOffsetState } from '../states/resizeFieldOffsetState';
|
import { resizeFieldOffsetState } from '../states/resizeFieldOffsetState';
|
||||||
import {
|
import { hiddenTableColumnsScopedSelector } from '../states/selectors/hiddenTableColumnsScopedSelector';
|
||||||
hiddenTableColumnsState,
|
import { tableColumnsByIdScopedSelector } from '../states/selectors/tableColumnsByIdScopedSelector';
|
||||||
tableColumnsByIdState,
|
import { visibleTableColumnsScopedSelector } from '../states/selectors/visibleTableColumnsScopedSelector';
|
||||||
tableColumnsState,
|
import { tableColumnsScopedState } from '../states/tableColumnsScopedState';
|
||||||
visibleTableColumnsState,
|
|
||||||
} from '../states/tableColumnsState';
|
|
||||||
|
|
||||||
import { ColumnHead } from './ColumnHead';
|
import { ColumnHead } from './ColumnHead';
|
||||||
import { EntityTableColumnMenu } from './EntityTableColumnMenu';
|
import { EntityTableColumnMenu } from './EntityTableColumnMenu';
|
||||||
@ -78,18 +75,26 @@ const StyledEntityTableColumnMenu = styled(EntityTableColumnMenu)`
|
|||||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export type EntityTableHeaderProps = {
|
export function EntityTableHeader() {
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [columns, setColumns] = useRecoilState(tableColumnsState);
|
|
||||||
const [offset, setOffset] = useRecoilState(resizeFieldOffsetState);
|
const [offset, setOffset] = useRecoilState(resizeFieldOffsetState);
|
||||||
const columnsById = useRecoilValue(tableColumnsByIdState);
|
const [columns, setColumns] = useRecoilScopedState(
|
||||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
tableColumnsScopedState,
|
||||||
const visibleColumns = useRecoilValue(visibleTableColumnsState);
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const columnsById = useRecoilScopedValue(
|
||||||
|
tableColumnsByIdScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const hiddenColumns = useRecoilScopedValue(
|
||||||
|
hiddenTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const visibleColumns = useRecoilScopedValue(
|
||||||
|
visibleTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
||||||
number | null
|
number | null
|
||||||
@ -129,14 +134,14 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
|||||||
: column,
|
: column,
|
||||||
);
|
);
|
||||||
|
|
||||||
(onColumnsChange ?? setColumns)(nextColumns);
|
setColumns(nextColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(resizeFieldOffsetState, 0);
|
set(resizeFieldOffsetState, 0);
|
||||||
setInitialPointerPositionX(null);
|
setInitialPointerPositionX(null);
|
||||||
setResizedFieldId(null);
|
setResizedFieldId(null);
|
||||||
},
|
},
|
||||||
[resizedFieldId, columnsById, columns, onColumnsChange, setColumns],
|
[resizedFieldId, columnsById, columns, setColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
useTrackPointer({
|
useTrackPointer({
|
||||||
@ -158,9 +163,9 @@ export function EntityTableHeader({ onColumnsChange }: EntityTableHeaderProps) {
|
|||||||
column.id === columnId ? { ...column, isVisible: true } : column,
|
column.id === columnId ? { ...column, isVisible: true } : column,
|
||||||
);
|
);
|
||||||
|
|
||||||
(onColumnsChange ?? setColumns)(nextColumns);
|
setColumns(nextColumns);
|
||||||
},
|
},
|
||||||
[columns, onColumnsChange, setColumns],
|
[columns, setColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
import { ViewFieldContext } from '../contexts/ViewFieldContext';
|
import { ViewFieldContext } from '../contexts/ViewFieldContext';
|
||||||
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
||||||
import { visibleTableColumnsState } from '../states/tableColumnsState';
|
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { visibleTableColumnsScopedSelector } from '../states/selectors/visibleTableColumnsScopedSelector';
|
||||||
|
|
||||||
import { CheckboxCell } from './CheckboxCell';
|
import { CheckboxCell } from './CheckboxCell';
|
||||||
import { EntityTableCell } from './EntityTableCell';
|
import { EntityTableCell } from './EntityTableCell';
|
||||||
@ -14,7 +16,10 @@ const StyledRow = styled.tr<{ selected: boolean }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function EntityTableRow({ rowId }: { rowId: string }) {
|
export function EntityTableRow({ rowId }: { rowId: string }) {
|
||||||
const columns = useRecoilValue(visibleTableColumnsState);
|
const columns = useRecoilScopedValue(
|
||||||
|
visibleTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
const { currentRowSelected } = useCurrentRowSelected();
|
const { currentRowSelected } = useCurrentRowSelected();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||||
|
|
||||||
import { numberOfTableRowsState } from '../states/numberOfTableRowsState';
|
import { numberOfTableRowsState } from '../states/numberOfTableRowsState';
|
||||||
|
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { numberOfTableColumnsScopedSelector } from '../states/selectors/numberOfTableColumnsScopedSelector';
|
||||||
import { softFocusPositionState } from '../states/softFocusPositionState';
|
import { softFocusPositionState } from '../states/softFocusPositionState';
|
||||||
import { numberOfTableColumnsState } from '../states/tableColumnsState';
|
|
||||||
|
|
||||||
import { useSetSoftFocusPosition } from './useSetSoftFocusPosition';
|
import { useSetSoftFocusPosition } from './useSetSoftFocusPosition';
|
||||||
|
|
||||||
// TODO: stories
|
// TODO: stories
|
||||||
export function useMoveSoftFocus() {
|
export function useMoveSoftFocus() {
|
||||||
|
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
|
||||||
const setSoftFocusPosition = useSetSoftFocusPosition();
|
const setSoftFocusPosition = useSetSoftFocusPosition();
|
||||||
|
|
||||||
const moveUp = useRecoilCallback(
|
const moveUp = useRecoilCallback(
|
||||||
@ -64,7 +68,7 @@ export function useMoveSoftFocus() {
|
|||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
const numberOfTableColumns = snapshot
|
const numberOfTableColumns = snapshot
|
||||||
.getLoadable(numberOfTableColumnsState)
|
.getLoadable(numberOfTableColumnsScopedSelector(tableScopeId))
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
const numberOfTableRows = snapshot
|
const numberOfTableRows = snapshot
|
||||||
@ -101,7 +105,7 @@ export function useMoveSoftFocus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setSoftFocusPosition],
|
[setSoftFocusPosition, tableScopeId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const moveLeft = useRecoilCallback(
|
const moveLeft = useRecoilCallback(
|
||||||
@ -112,7 +116,7 @@ export function useMoveSoftFocus() {
|
|||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
const numberOfTableColumns = snapshot
|
const numberOfTableColumns = snapshot
|
||||||
.getLoadable(numberOfTableColumnsState)
|
.getLoadable(numberOfTableColumnsScopedSelector(tableScopeId))
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
const currentColumnNumber = softFocusPosition.column;
|
const currentColumnNumber = softFocusPosition.column;
|
||||||
@ -142,7 +146,7 @@ export function useMoveSoftFocus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setSoftFocusPosition],
|
[setSoftFocusPosition, tableScopeId],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
|
|
||||||
import { type TableView } from '../../states/tableViewsState';
|
import { type TableView } from '../../states/tableViewsState';
|
||||||
@ -11,14 +7,12 @@ import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
|
|||||||
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
|
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
|
||||||
|
|
||||||
type TableOptionsDropdownProps = {
|
type TableOptionsDropdownProps = {
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
onImport?: () => void;
|
onImport?: () => void;
|
||||||
customHotkeyScope: HotkeyScope;
|
customHotkeyScope: HotkeyScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TableOptionsDropdown({
|
export function TableOptionsDropdown({
|
||||||
onColumnsChange,
|
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
onImport,
|
onImport,
|
||||||
customHotkeyScope,
|
customHotkeyScope,
|
||||||
@ -30,7 +24,6 @@ export function TableOptionsDropdown({
|
|||||||
dropdownKey="options"
|
dropdownKey="options"
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<TableOptionsDropdownContent
|
<TableOptionsDropdownContent
|
||||||
onColumnsChange={onColumnsChange}
|
|
||||||
onImport={onImport}
|
onImport={onImport}
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { type FormEvent, useCallback, useRef, useState } from 'react';
|
import { type FormEvent, useCallback, useRef, useState } from 'react';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
@ -27,16 +27,16 @@ import {
|
|||||||
IconPlus,
|
IconPlus,
|
||||||
IconTag,
|
IconTag,
|
||||||
} from '@/ui/icon';
|
} from '@/ui/icon';
|
||||||
import {
|
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||||
hiddenTableColumnsState,
|
|
||||||
tableColumnsState,
|
|
||||||
visibleTableColumnsState,
|
|
||||||
} from '@/ui/table/states/tableColumnsState';
|
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||||
|
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||||
|
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||||
import {
|
import {
|
||||||
currentTableViewIdState,
|
currentTableViewIdState,
|
||||||
type TableView,
|
type TableView,
|
||||||
@ -49,7 +49,6 @@ import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
|||||||
import { TableOptionsDropdownSection } from './TableOptionsDropdownSection';
|
import { TableOptionsDropdownSection } from './TableOptionsDropdownSection';
|
||||||
|
|
||||||
type TableOptionsDropdownButtonProps = {
|
type TableOptionsDropdownButtonProps = {
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
onImport?: () => void;
|
onImport?: () => void;
|
||||||
};
|
};
|
||||||
@ -59,7 +58,6 @@ enum Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TableOptionsDropdownContent({
|
export function TableOptionsDropdownContent({
|
||||||
onColumnsChange,
|
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
onImport,
|
onImport,
|
||||||
}: TableOptionsDropdownButtonProps) {
|
}: TableOptionsDropdownButtonProps) {
|
||||||
@ -75,28 +73,37 @@ export function TableOptionsDropdownContent({
|
|||||||
|
|
||||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [columns, setColumns] = useRecoilState(tableColumnsState);
|
|
||||||
const [viewEditMode, setViewEditMode] = useRecoilState(
|
const [viewEditMode, setViewEditMode] = useRecoilState(
|
||||||
tableViewEditModeState,
|
tableViewEditModeState,
|
||||||
);
|
);
|
||||||
const visibleColumns = useRecoilValue(visibleTableColumnsState);
|
const [columns, setColumns] = useRecoilScopedState(
|
||||||
const hiddenColumns = useRecoilValue(hiddenTableColumnsState);
|
tableColumnsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const visibleColumns = useRecoilScopedValue(
|
||||||
|
visibleTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const hiddenColumns = useRecoilScopedValue(
|
||||||
|
hiddenTableColumnsScopedSelector,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
const viewsById = useRecoilScopedValue(
|
const viewsById = useRecoilScopedValue(
|
||||||
tableViewsByIdState,
|
tableViewsByIdState,
|
||||||
TableRecoilScopeContext,
|
TableRecoilScopeContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleColumnVisibilityChange = useCallback(
|
const handleColumnVisibilityChange = useCallback(
|
||||||
(columnId: string, nextIsVisible: boolean) => {
|
async (columnId: string, nextIsVisible: boolean) => {
|
||||||
const nextColumns = columns.map((column) =>
|
const nextColumns = columns.map((column) =>
|
||||||
column.id === columnId
|
column.id === columnId
|
||||||
? { ...column, isVisible: nextIsVisible }
|
? { ...column, isVisible: nextIsVisible }
|
||||||
: column,
|
: column,
|
||||||
);
|
);
|
||||||
|
|
||||||
(onColumnsChange ?? setColumns)(nextColumns);
|
setColumns(nextColumns);
|
||||||
},
|
},
|
||||||
[columns, onColumnsChange, setColumns],
|
[columns, setColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderFieldActions = useCallback(
|
const renderFieldActions = useCallback(
|
||||||
@ -144,6 +151,14 @@ export function TableOptionsDropdownContent({
|
|||||||
const viewToCreate = { id: v4(), name };
|
const viewToCreate = { id: v4(), name };
|
||||||
const nextViews = [...views, viewToCreate];
|
const nextViews = [...views, viewToCreate];
|
||||||
|
|
||||||
|
const currentColumns = await snapshot.getPromise(
|
||||||
|
tableColumnsScopedState(tableScopeId),
|
||||||
|
);
|
||||||
|
set(
|
||||||
|
savedTableColumnsScopedState(viewToCreate.id),
|
||||||
|
currentColumns.map((column) => ({ ...column, id: v4() })),
|
||||||
|
);
|
||||||
|
|
||||||
const selectedFilters = await snapshot.getPromise(
|
const selectedFilters = await snapshot.getPromise(
|
||||||
filtersScopedState(tableScopeId),
|
filtersScopedState(tableScopeId),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { Button, ButtonSize } from '@/ui/button/components/Button';
|
import { Button, ButtonSize } from '@/ui/button/components/Button';
|
||||||
@ -22,6 +22,9 @@ import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextS
|
|||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||||
|
import { canPersistTableColumnsScopedSelector } from '../../states/selectors/canPersistTableColumnsScopedSelector';
|
||||||
|
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||||
import {
|
import {
|
||||||
currentTableViewIdState,
|
currentTableViewIdState,
|
||||||
tableViewEditModeState,
|
tableViewEditModeState,
|
||||||
@ -56,12 +59,38 @@ export const TableUpdateViewButtonGroup = ({
|
|||||||
currentTableViewIdState,
|
currentTableViewIdState,
|
||||||
TableRecoilScopeContext,
|
TableRecoilScopeContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentColumns = useRecoilScopedValue(
|
||||||
|
tableColumnsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const setSavedColumns = useSetRecoilState(
|
||||||
|
savedTableColumnsScopedState(currentViewId),
|
||||||
|
);
|
||||||
|
const canPersistColumns = useRecoilValue(
|
||||||
|
canPersistTableColumnsScopedSelector([tableScopeId, currentViewId]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedFilters = useRecoilScopedValue(
|
||||||
|
filtersScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const setSavedFilters = useSetRecoilState(
|
||||||
|
savedFiltersScopedState(currentViewId),
|
||||||
|
);
|
||||||
const canPersistFilters = useRecoilValue(
|
const canPersistFilters = useRecoilValue(
|
||||||
canPersistFiltersScopedSelector([tableScopeId, currentViewId]),
|
canPersistFiltersScopedSelector([tableScopeId, currentViewId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectedSorts = useRecoilScopedValue(
|
||||||
|
sortsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const setSavedSorts = useSetRecoilState(savedSortsScopedState(currentViewId));
|
||||||
const canPersistSorts = useRecoilValue(
|
const canPersistSorts = useRecoilValue(
|
||||||
canPersistSortsScopedSelector([tableScopeId, currentViewId]),
|
canPersistSortsScopedSelector([tableScopeId, currentViewId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const setViewEditMode = useSetRecoilState(tableViewEditModeState);
|
const setViewEditMode = useSetRecoilState(tableViewEditModeState);
|
||||||
|
|
||||||
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
|
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
|
||||||
@ -82,23 +111,21 @@ export const TableUpdateViewButtonGroup = ({
|
|||||||
setIsDropdownOpen(false);
|
setIsDropdownOpen(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleViewSubmit = useRecoilCallback(
|
const handleViewSubmit = useCallback(async () => {
|
||||||
({ set, snapshot }) =>
|
await Promise.resolve(onViewSubmit?.());
|
||||||
async () => {
|
|
||||||
await Promise.resolve(onViewSubmit?.());
|
|
||||||
|
|
||||||
const selectedFilters = await snapshot.getPromise(
|
setSavedColumns(currentColumns);
|
||||||
filtersScopedState(tableScopeId),
|
setSavedFilters(selectedFilters);
|
||||||
);
|
setSavedSorts(selectedSorts);
|
||||||
set(savedFiltersScopedState(currentViewId), selectedFilters);
|
}, [
|
||||||
|
currentColumns,
|
||||||
const selectedSorts = await snapshot.getPromise(
|
onViewSubmit,
|
||||||
sortsScopedState(tableScopeId),
|
selectedFilters,
|
||||||
);
|
selectedSorts,
|
||||||
set(savedSortsScopedState(currentViewId), selectedSorts);
|
setSavedColumns,
|
||||||
},
|
setSavedFilters,
|
||||||
[currentViewId, onViewSubmit, tableScopeId],
|
setSavedSorts,
|
||||||
);
|
]);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.Enter, Key.Escape],
|
[Key.Enter, Key.Escape],
|
||||||
@ -112,7 +139,10 @@ export const TableUpdateViewButtonGroup = ({
|
|||||||
<ButtonGroup size={ButtonSize.Small}>
|
<ButtonGroup size={ButtonSize.Small}>
|
||||||
<Button
|
<Button
|
||||||
title="Update view"
|
title="Update view"
|
||||||
disabled={!currentViewId || (!canPersistFilters && !canPersistSorts)}
|
disabled={
|
||||||
|
!currentViewId ||
|
||||||
|
(!canPersistColumns && !canPersistFilters && !canPersistSorts)
|
||||||
|
}
|
||||||
onClick={handleViewSubmit}
|
onClick={handleViewSubmit}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -33,6 +33,8 @@ import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoi
|
|||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { savedTableColumnsScopedState } from '../../states/savedTableColumnsScopedState';
|
||||||
|
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||||
import { TableViewsHotkeyScope } from '../../types/TableViewsHotkeyScope';
|
import { TableViewsHotkeyScope } from '../../types/TableViewsHotkeyScope';
|
||||||
|
|
||||||
const StyledBoldDropdownMenuItemsContainer = styled(
|
const StyledBoldDropdownMenuItemsContainer = styled(
|
||||||
@ -95,6 +97,9 @@ export const TableViewsDropdownButton = ({
|
|||||||
const handleViewSelect = useRecoilCallback(
|
const handleViewSelect = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
async (viewId?: string) => {
|
async (viewId?: string) => {
|
||||||
|
const savedColumns = await snapshot.getPromise(
|
||||||
|
savedTableColumnsScopedState(viewId),
|
||||||
|
);
|
||||||
const savedFilters = await snapshot.getPromise(
|
const savedFilters = await snapshot.getPromise(
|
||||||
savedFiltersScopedState(viewId),
|
savedFiltersScopedState(viewId),
|
||||||
);
|
);
|
||||||
@ -102,6 +107,7 @@ export const TableViewsDropdownButton = ({
|
|||||||
savedSortsScopedState(viewId),
|
savedSortsScopedState(viewId),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
set(tableColumnsScopedState(tableScopeId), savedColumns);
|
||||||
set(filtersScopedState(tableScopeId), savedFilters);
|
set(filtersScopedState(tableScopeId), savedFilters);
|
||||||
set(sortsScopedState(tableScopeId), savedSorts);
|
set(sortsScopedState(tableScopeId), savedSorts);
|
||||||
set(currentTableViewIdState(tableScopeId), viewId);
|
set(currentTableViewIdState(tableScopeId), viewId);
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
export const savedTableColumnsScopedState = atomFamily<
|
||||||
|
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
string | undefined
|
||||||
|
>({
|
||||||
|
key: 'savedTableColumnsScopedState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
|
import { savedTableColumnsScopedState } from '../savedTableColumnsScopedState';
|
||||||
|
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||||
|
|
||||||
|
export const canPersistTableColumnsScopedSelector = selectorFamily({
|
||||||
|
key: 'canPersistTableColumnsScopedSelector',
|
||||||
|
get:
|
||||||
|
([scopeId, viewId]: [string, string | undefined]) =>
|
||||||
|
({ get }) =>
|
||||||
|
!isDeeplyEqual(
|
||||||
|
get(savedTableColumnsScopedState(viewId)),
|
||||||
|
get(tableColumnsScopedState(scopeId)),
|
||||||
|
),
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||||
|
|
||||||
|
export const hiddenTableColumnsScopedSelector = selectorFamily({
|
||||||
|
key: 'hiddenTableColumnsScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(tableColumnsScopedState(scopeId)).filter(
|
||||||
|
(column) => !column.isVisible,
|
||||||
|
),
|
||||||
|
});
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||||
|
|
||||||
|
export const numberOfTableColumnsScopedSelector = selectorFamily({
|
||||||
|
key: 'numberOfTableColumnsScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(tableColumnsScopedState(scopeId)).length,
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
import { savedTableColumnsScopedState } from '../savedTableColumnsScopedState';
|
||||||
|
|
||||||
|
export const savedTableColumnsByIdScopedSelector = selectorFamily({
|
||||||
|
key: 'savedTableColumnsByIdScopedSelector',
|
||||||
|
get:
|
||||||
|
(viewId: string | undefined) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(savedTableColumnsScopedState(viewId)).reduce<
|
||||||
|
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||||
|
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||||
|
|
||||||
|
export const tableColumnsByIdScopedSelector = selectorFamily({
|
||||||
|
key: 'tableColumnsByIdScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(tableColumnsScopedState(scopeId)).reduce<
|
||||||
|
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||||
|
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { tableColumnsScopedState } from '../tableColumnsScopedState';
|
||||||
|
|
||||||
|
export const visibleTableColumnsScopedSelector = selectorFamily({
|
||||||
|
key: 'visibleTableColumnsScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(tableColumnsScopedState(scopeId)).filter(
|
||||||
|
(column) => column.isVisible,
|
||||||
|
),
|
||||||
|
});
|
||||||
14
front/src/modules/ui/table/states/tableColumnsScopedState.ts
Normal file
14
front/src/modules/ui/table/states/tableColumnsScopedState.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
export const tableColumnsScopedState = atomFamily<
|
||||||
|
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'tableColumnsScopedState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -1,37 +0,0 @@
|
|||||||
import { atom, selector } from 'recoil';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
|
||||||
|
|
||||||
export const tableColumnsState = atom<ViewFieldDefinition<ViewFieldMetadata>[]>(
|
|
||||||
{
|
|
||||||
key: 'tableColumnsState',
|
|
||||||
default: [],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const tableColumnsByIdState = selector({
|
|
||||||
key: 'tableColumnsByIdState',
|
|
||||||
get: ({ get }) =>
|
|
||||||
get(tableColumnsState).reduce<
|
|
||||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
|
||||||
>((result, column) => ({ ...result, [column.id]: column }), {}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const numberOfTableColumnsState = selector<number>({
|
|
||||||
key: 'numberOfTableColumnsState',
|
|
||||||
get: ({ get }) => get(tableColumnsState).length,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const visibleTableColumnsState = selector({
|
|
||||||
key: 'visibleTableColumnsState',
|
|
||||||
get: ({ get }) => get(tableColumnsState).filter((column) => column.isVisible),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const hiddenTableColumnsState = selector({
|
|
||||||
key: 'hiddenTableColumnsState',
|
|
||||||
get: ({ get }) =>
|
|
||||||
get(tableColumnsState).filter((column) => !column.isVisible),
|
|
||||||
});
|
|
||||||
@ -1,10 +1,6 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { FilterDropdownButton } from '@/ui/filter-n-sort/components/FilterDropdownButton';
|
import { FilterDropdownButton } from '@/ui/filter-n-sort/components/FilterDropdownButton';
|
||||||
import SortAndFilterBar from '@/ui/filter-n-sort/components/SortAndFilterBar';
|
import SortAndFilterBar from '@/ui/filter-n-sort/components/SortAndFilterBar';
|
||||||
import { SortDropdownButton } from '@/ui/filter-n-sort/components/SortDropdownButton';
|
import { SortDropdownButton } from '@/ui/filter-n-sort/components/SortDropdownButton';
|
||||||
@ -26,7 +22,6 @@ import { TableViewsHotkeyScope } from '../../types/TableViewsHotkeyScope';
|
|||||||
type OwnProps<SortField> = {
|
type OwnProps<SortField> = {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
availableSorts?: Array<SortType<SortField>>;
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
onColumnsChange?: (columns: ViewFieldDefinition<ViewFieldMetadata>[]) => void;
|
|
||||||
onViewsChange?: (views: TableView[]) => void;
|
onViewsChange?: (views: TableView[]) => void;
|
||||||
onViewSubmit?: () => void;
|
onViewSubmit?: () => void;
|
||||||
onImport?: () => void;
|
onImport?: () => void;
|
||||||
@ -35,7 +30,6 @@ type OwnProps<SortField> = {
|
|||||||
export function TableHeader<SortField>({
|
export function TableHeader<SortField>({
|
||||||
viewName,
|
viewName,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
onColumnsChange,
|
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
onViewSubmit,
|
onViewSubmit,
|
||||||
onImport,
|
onImport,
|
||||||
@ -89,7 +83,6 @@ export function TableHeader<SortField>({
|
|||||||
/>
|
/>
|
||||||
<TableOptionsDropdown
|
<TableOptionsDropdown
|
||||||
onImport={onImport}
|
onImport={onImport}
|
||||||
onColumnsChange={onColumnsChange}
|
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { getOperationName } from '@apollo/client/utilities';
|
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import {
|
import type {
|
||||||
ViewFieldDefinition,
|
ViewFieldDefinition,
|
||||||
ViewFieldMetadata,
|
ViewFieldMetadata,
|
||||||
ViewFieldTextMetadata,
|
ViewFieldTextMetadata,
|
||||||
} from '@/ui/editable-field/types/ViewField';
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import {
|
import { savedTableColumnsScopedState } from '@/ui/table/states/savedTableColumnsScopedState';
|
||||||
tableColumnsByIdState,
|
import { savedTableColumnsByIdScopedSelector } from '@/ui/table/states/selectors/savedTableColumnsByIdScopedSelector';
|
||||||
tableColumnsState,
|
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||||
} from '@/ui/table/states/tableColumnsState';
|
|
||||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import {
|
import {
|
||||||
SortOrder,
|
SortOrder,
|
||||||
@ -20,8 +19,7 @@ import {
|
|||||||
useGetViewFieldsQuery,
|
useGetViewFieldsQuery,
|
||||||
useUpdateViewFieldMutation,
|
useUpdateViewFieldMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
import { GET_VIEW_FIELDS } from '../graphql/queries/getViewFields';
|
|
||||||
|
|
||||||
const DEFAULT_VIEW_FIELD_METADATA: ViewFieldTextMetadata = {
|
const DEFAULT_VIEW_FIELD_METADATA: ViewFieldTextMetadata = {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -51,24 +49,34 @@ export const useTableViewFields = ({
|
|||||||
currentTableViewIdState,
|
currentTableViewIdState,
|
||||||
TableRecoilScopeContext,
|
TableRecoilScopeContext,
|
||||||
);
|
);
|
||||||
const setColumns = useSetRecoilState(tableColumnsState);
|
const [columns, setColumns] = useRecoilScopedState(
|
||||||
const columnsById = useRecoilValue(tableColumnsByIdState);
|
tableColumnsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const setSavedColumns = useSetRecoilState(
|
||||||
|
savedTableColumnsScopedState(currentViewId),
|
||||||
|
);
|
||||||
|
const savedColumnsById = useRecoilValue(
|
||||||
|
savedTableColumnsByIdScopedSelector(currentViewId),
|
||||||
|
);
|
||||||
|
|
||||||
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
|
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
|
||||||
const [updateViewFieldMutation] = useUpdateViewFieldMutation();
|
const [updateViewFieldMutation] = useUpdateViewFieldMutation();
|
||||||
|
|
||||||
const createViewFields = useCallback(
|
const createViewFields = useCallback(
|
||||||
(columns: ViewFieldDefinition<ViewFieldMetadata>[]) => {
|
(
|
||||||
|
columns: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
viewId = currentViewId,
|
||||||
|
) => {
|
||||||
if (!columns.length) return;
|
if (!columns.length) return;
|
||||||
|
|
||||||
return createViewFieldsMutation({
|
return createViewFieldsMutation({
|
||||||
variables: {
|
variables: {
|
||||||
data: columns.map((column) => ({
|
data: columns.map((column) => ({
|
||||||
...toViewFieldInput(objectName, column),
|
...toViewFieldInput(objectName, column),
|
||||||
viewId: currentViewId,
|
viewId,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[createViewFieldsMutation, currentViewId, objectName],
|
[createViewFieldsMutation, currentViewId, objectName],
|
||||||
@ -88,7 +96,6 @@ export const useTableViewFields = ({
|
|||||||
},
|
},
|
||||||
where: { id: column.id },
|
where: { id: column.id },
|
||||||
},
|
},
|
||||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -96,7 +103,7 @@ export const useTableViewFields = ({
|
|||||||
[updateViewFieldMutation],
|
[updateViewFieldMutation],
|
||||||
);
|
);
|
||||||
|
|
||||||
useGetViewFieldsQuery({
|
const { refetch } = useGetViewFieldsQuery({
|
||||||
variables: {
|
variables: {
|
||||||
orderBy: { index: SortOrder.Asc },
|
orderBy: { index: SortOrder.Asc },
|
||||||
where: {
|
where: {
|
||||||
@ -104,50 +111,44 @@ export const useTableViewFields = ({
|
|||||||
viewId: { equals: currentViewId ?? null },
|
viewId: { equals: currentViewId ?? null },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted: (data) => {
|
onCompleted: async (data) => {
|
||||||
if (data.viewFields.length) {
|
if (!data.viewFields.length) {
|
||||||
setColumns(
|
// Populate if empty
|
||||||
data.viewFields.map<ViewFieldDefinition<ViewFieldMetadata>>(
|
await createViewFields(viewFieldDefinitions);
|
||||||
(viewField) => ({
|
return refetch();
|
||||||
...(viewFieldDefinitions.find(
|
|
||||||
({ columnLabel }) => viewField.fieldName === columnLabel,
|
|
||||||
) || { metadata: DEFAULT_VIEW_FIELD_METADATA }),
|
|
||||||
id: viewField.id,
|
|
||||||
columnLabel: viewField.fieldName,
|
|
||||||
columnOrder: viewField.index,
|
|
||||||
columnSize: viewField.sizeInPx,
|
|
||||||
isVisible: viewField.isVisible,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate if empty
|
const nextColumns = data.viewFields.map<
|
||||||
createViewFields(viewFieldDefinitions);
|
ViewFieldDefinition<ViewFieldMetadata>
|
||||||
|
>((viewField) => ({
|
||||||
|
...(viewFieldDefinitions.find(
|
||||||
|
({ columnLabel }) => viewField.fieldName === columnLabel,
|
||||||
|
) || { metadata: DEFAULT_VIEW_FIELD_METADATA }),
|
||||||
|
id: viewField.id,
|
||||||
|
columnLabel: viewField.fieldName,
|
||||||
|
columnOrder: viewField.index,
|
||||||
|
columnSize: viewField.sizeInPx,
|
||||||
|
isVisible: viewField.isVisible,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!isDeeplyEqual(columns, nextColumns)) {
|
||||||
|
setSavedColumns(nextColumns);
|
||||||
|
setColumns(nextColumns);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleColumnsChange = useCallback(
|
const persistColumns = useCallback(async () => {
|
||||||
async (nextColumns: ViewFieldDefinition<ViewFieldMetadata>[]) => {
|
const viewFieldsToUpdate = columns.filter(
|
||||||
setColumns(nextColumns);
|
(column) =>
|
||||||
|
savedColumnsById[column.id] &&
|
||||||
|
(savedColumnsById[column.id].columnSize !== column.columnSize ||
|
||||||
|
savedColumnsById[column.id].isVisible !== column.isVisible),
|
||||||
|
);
|
||||||
|
await updateViewFields(viewFieldsToUpdate);
|
||||||
|
|
||||||
const viewFieldsToCreate = nextColumns.filter(
|
return refetch();
|
||||||
(nextColumn) => !columnsById[nextColumn.id],
|
}, [columns, refetch, savedColumnsById, updateViewFields]);
|
||||||
);
|
|
||||||
await createViewFields(viewFieldsToCreate);
|
|
||||||
|
|
||||||
const viewFieldsToUpdate = nextColumns.filter(
|
return { createViewFields, persistColumns };
|
||||||
(nextColumn) =>
|
|
||||||
columnsById[nextColumn.id] &&
|
|
||||||
(columnsById[nextColumn.id].columnSize !== nextColumn.columnSize ||
|
|
||||||
columnsById[nextColumn.id].isVisible !== nextColumn.isVisible),
|
|
||||||
);
|
|
||||||
await updateViewFields(viewFieldsToUpdate);
|
|
||||||
},
|
|
||||||
[columnsById, createViewFields, setColumns, updateViewFields],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { handleColumnsChange };
|
|
||||||
};
|
};
|
||||||
|
|||||||
92
front/src/modules/views/hooks/useTableViews.ts
Normal file
92
front/src/modules/views/hooks/useTableViews.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
|
import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState';
|
||||||
|
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
||||||
|
import type { SortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
|
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||||
|
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
|
import { useTableViewFields } from './useTableViewFields';
|
||||||
|
import { useViewFilters } from './useViewFilters';
|
||||||
|
import { useViews } from './useViews';
|
||||||
|
import { useViewSorts } from './useViewSorts';
|
||||||
|
|
||||||
|
export const useTableViews = <Entity, SortField>({
|
||||||
|
availableFilters,
|
||||||
|
availableSorts,
|
||||||
|
objectId,
|
||||||
|
viewFieldDefinitions,
|
||||||
|
}: {
|
||||||
|
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||||
|
availableSorts: SortType<SortField>[];
|
||||||
|
objectId: 'company' | 'person';
|
||||||
|
viewFieldDefinitions: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||||
|
}) => {
|
||||||
|
const currentViewId = useRecoilScopedValue(
|
||||||
|
currentTableViewIdState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const currentColumns = useRecoilScopedValue(
|
||||||
|
tableColumnsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const selectedFilters = useRecoilScopedValue(
|
||||||
|
filtersScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
const selectedSorts = useRecoilScopedValue(
|
||||||
|
sortsScopedState,
|
||||||
|
TableRecoilScopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { createViewFields, persistColumns } = useTableViewFields({
|
||||||
|
objectName: objectId,
|
||||||
|
viewFieldDefinitions,
|
||||||
|
});
|
||||||
|
const { createViewFilters, persistFilters } = useViewFilters({
|
||||||
|
availableFilters,
|
||||||
|
currentViewId,
|
||||||
|
scopeContext: TableRecoilScopeContext,
|
||||||
|
});
|
||||||
|
const { createViewSorts, persistSorts } = useViewSorts({
|
||||||
|
availableSorts,
|
||||||
|
currentViewId,
|
||||||
|
scopeContext: TableRecoilScopeContext,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleViewCreate = useCallback(
|
||||||
|
async (viewId: string) => {
|
||||||
|
await createViewFields(currentColumns, viewId);
|
||||||
|
await createViewFilters(selectedFilters, viewId);
|
||||||
|
await createViewSorts(selectedSorts, viewId);
|
||||||
|
},
|
||||||
|
[
|
||||||
|
createViewFields,
|
||||||
|
createViewFilters,
|
||||||
|
createViewSorts,
|
||||||
|
currentColumns,
|
||||||
|
selectedFilters,
|
||||||
|
selectedSorts,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleViewSubmit = useCallback(async () => {
|
||||||
|
await persistColumns();
|
||||||
|
await persistFilters();
|
||||||
|
await persistSorts();
|
||||||
|
}, [persistColumns, persistFilters, persistSorts]);
|
||||||
|
|
||||||
|
const { handleViewsChange } = useViews({
|
||||||
|
objectId,
|
||||||
|
onViewCreate: handleViewCreate,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { handleViewsChange, handleViewSubmit };
|
||||||
|
};
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { Context, useCallback } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
@ -6,10 +6,7 @@ import { savedFiltersScopedState } from '@/ui/filter-n-sort/states/savedFiltersS
|
|||||||
import { savedFiltersByKeyScopedSelector } from '@/ui/filter-n-sort/states/selectors/savedFiltersByKeyScopedSelector';
|
import { savedFiltersByKeyScopedSelector } from '@/ui/filter-n-sort/states/selectors/savedFiltersByKeyScopedSelector';
|
||||||
import type { Filter } from '@/ui/filter-n-sort/types/Filter';
|
import type { Filter } from '@/ui/filter-n-sort/types/Filter';
|
||||||
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
|
||||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
|
||||||
import {
|
import {
|
||||||
useCreateViewFiltersMutation,
|
useCreateViewFiltersMutation,
|
||||||
useDeleteViewFiltersMutation,
|
useDeleteViewFiltersMutation,
|
||||||
@ -20,16 +17,16 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|||||||
|
|
||||||
export const useViewFilters = <Entity>({
|
export const useViewFilters = <Entity>({
|
||||||
availableFilters,
|
availableFilters,
|
||||||
|
currentViewId,
|
||||||
|
scopeContext,
|
||||||
}: {
|
}: {
|
||||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||||
|
currentViewId: string | undefined;
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
}) => {
|
}) => {
|
||||||
const currentViewId = useRecoilScopedValue(
|
|
||||||
currentTableViewIdState,
|
|
||||||
TableRecoilScopeContext,
|
|
||||||
);
|
|
||||||
const [filters, setFilters] = useRecoilScopedState(
|
const [filters, setFilters] = useRecoilScopedState(
|
||||||
filtersScopedState,
|
filtersScopedState,
|
||||||
TableRecoilScopeContext,
|
scopeContext,
|
||||||
);
|
);
|
||||||
const [, setSavedFilters] = useRecoilState(
|
const [, setSavedFilters] = useRecoilState(
|
||||||
savedFiltersScopedState(currentViewId),
|
savedFiltersScopedState(currentViewId),
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { Context, useCallback } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { savedSortsScopedState } from '@/ui/filter-n-sort/states/savedSortsScopedState';
|
import { savedSortsScopedState } from '@/ui/filter-n-sort/states/savedSortsScopedState';
|
||||||
@ -8,10 +8,7 @@ import type {
|
|||||||
SelectedSortType,
|
SelectedSortType,
|
||||||
SortType,
|
SortType,
|
||||||
} from '@/ui/filter-n-sort/types/interface';
|
} from '@/ui/filter-n-sort/types/interface';
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
|
||||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
|
||||||
import {
|
import {
|
||||||
useCreateViewSortsMutation,
|
useCreateViewSortsMutation,
|
||||||
useDeleteViewSortsMutation,
|
useDeleteViewSortsMutation,
|
||||||
@ -23,16 +20,16 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|||||||
|
|
||||||
export const useViewSorts = <SortField>({
|
export const useViewSorts = <SortField>({
|
||||||
availableSorts,
|
availableSorts,
|
||||||
|
currentViewId,
|
||||||
|
scopeContext,
|
||||||
}: {
|
}: {
|
||||||
availableSorts: SortType<SortField>[];
|
availableSorts: SortType<SortField>[];
|
||||||
|
currentViewId: string | undefined;
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
}) => {
|
}) => {
|
||||||
const currentViewId = useRecoilScopedValue(
|
|
||||||
currentTableViewIdState,
|
|
||||||
TableRecoilScopeContext,
|
|
||||||
);
|
|
||||||
const [sorts, setSorts] = useRecoilScopedState(
|
const [sorts, setSorts] = useRecoilScopedState(
|
||||||
sortsScopedState,
|
sortsScopedState,
|
||||||
TableRecoilScopeContext,
|
scopeContext,
|
||||||
);
|
);
|
||||||
const [, setSavedSorts] = useRecoilState(
|
const [, setSavedSorts] = useRecoilState(
|
||||||
savedSortsScopedState(currentViewId),
|
savedSortsScopedState(currentViewId),
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
|
||||||
import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState';
|
|
||||||
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
|
||||||
import type { SortType } from '@/ui/filter-n-sort/types/interface';
|
|
||||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import {
|
import {
|
||||||
type TableView,
|
type TableView,
|
||||||
@ -21,17 +17,12 @@ import {
|
|||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
import { useViewFilters } from './useViewFilters';
|
export const useViews = ({
|
||||||
import { useViewSorts } from './useViewSorts';
|
|
||||||
|
|
||||||
export const useViews = <Entity, SortField>({
|
|
||||||
availableFilters,
|
|
||||||
availableSorts,
|
|
||||||
objectId,
|
objectId,
|
||||||
|
onViewCreate,
|
||||||
}: {
|
}: {
|
||||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
|
||||||
availableSorts: SortType<SortField>[];
|
|
||||||
objectId: 'company' | 'person';
|
objectId: 'company' | 'person';
|
||||||
|
onViewCreate: (viewId: string) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const [views, setViews] = useRecoilScopedState(
|
const [views, setViews] = useRecoilScopedState(
|
||||||
tableViewsState,
|
tableViewsState,
|
||||||
@ -41,22 +32,11 @@ export const useViews = <Entity, SortField>({
|
|||||||
tableViewsByIdState,
|
tableViewsByIdState,
|
||||||
TableRecoilScopeContext,
|
TableRecoilScopeContext,
|
||||||
);
|
);
|
||||||
const selectedFilters = useRecoilScopedValue(
|
|
||||||
filtersScopedState,
|
|
||||||
TableRecoilScopeContext,
|
|
||||||
);
|
|
||||||
const selectedSorts = useRecoilScopedValue(
|
|
||||||
sortsScopedState,
|
|
||||||
TableRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [createViewsMutation] = useCreateViewsMutation();
|
const [createViewsMutation] = useCreateViewsMutation();
|
||||||
const [updateViewMutation] = useUpdateViewMutation();
|
const [updateViewMutation] = useUpdateViewMutation();
|
||||||
const [deleteViewsMutation] = useDeleteViewsMutation();
|
const [deleteViewsMutation] = useDeleteViewsMutation();
|
||||||
|
|
||||||
const { createViewFilters } = useViewFilters({ availableFilters });
|
|
||||||
const { createViewSorts } = useViewSorts({ availableSorts });
|
|
||||||
|
|
||||||
const createViews = useCallback(
|
const createViews = useCallback(
|
||||||
async (views: TableView[]) => {
|
async (views: TableView[]) => {
|
||||||
if (!views.length) return;
|
if (!views.length) return;
|
||||||
@ -71,21 +51,9 @@ export const useViews = <Entity, SortField>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(views.map((view) => onViewCreate(view.id)));
|
||||||
views.flatMap((view) => [
|
|
||||||
createViewFilters(selectedFilters, view.id),
|
|
||||||
createViewSorts(selectedSorts, view.id),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[
|
[createViewsMutation, objectId, onViewCreate],
|
||||||
createViewFilters,
|
|
||||||
createViewSorts,
|
|
||||||
createViewsMutation,
|
|
||||||
objectId,
|
|
||||||
selectedFilters,
|
|
||||||
selectedSorts,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateViews = useCallback(
|
const updateViews = useCallback(
|
||||||
|
|||||||
Reference in New Issue
Block a user