mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-04 18:21:59 +03:00
parent
6462505a86
commit
2461a387ce
@ -5,6 +5,7 @@ import {
|
||||
} from '@/ui/board/components/EntityBoard';
|
||||
import { EntityBoardActionBar } from '@/ui/board/components/EntityBoardActionBar';
|
||||
import { EntityBoardContextMenu } from '@/ui/board/components/EntityBoardContextMenu';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { useBoardViews } from '@/views/hooks/useBoardViews';
|
||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||
|
||||
@ -16,25 +17,38 @@ type CompanyBoardProps = Pick<
|
||||
'onColumnAdd' | 'onColumnDelete' | 'onEditColumnTitle'
|
||||
>;
|
||||
|
||||
export const CompanyBoard = ({ ...props }: CompanyBoardProps) => {
|
||||
const { handleViewsChange, handleViewSubmit } = useBoardViews({
|
||||
objectId: 'company',
|
||||
scopeContext: CompanyBoardRecoilScopeContext,
|
||||
fieldDefinitions: pipelineAvailableFieldDefinitions,
|
||||
});
|
||||
export const CompanyBoard = ({
|
||||
onColumnAdd,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
}: CompanyBoardProps) => {
|
||||
const { createView, deleteView, submitCurrentView, updateView } =
|
||||
useBoardViews({
|
||||
objectId: 'company',
|
||||
scopeContext: CompanyBoardRecoilScopeContext,
|
||||
fieldDefinitions: pipelineAvailableFieldDefinitions,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<HooksCompanyBoard />
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
defaultViewName="All opportunities"
|
||||
onViewsChange={handleViewsChange}
|
||||
onViewSubmit={handleViewSubmit}
|
||||
onColumnAdd={props.onColumnAdd}
|
||||
scopeContext={CompanyBoardRecoilScopeContext}
|
||||
onEditColumnTitle={props.onEditColumnTitle}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All Opportunities',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
}}
|
||||
>
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
onColumnAdd={onColumnAdd}
|
||||
onColumnDelete={onColumnDelete}
|
||||
onEditColumnTitle={onEditColumnTitle}
|
||||
scopeContext={CompanyBoardRecoilScopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
<EntityBoardActionBar />
|
||||
<EntityBoardContextMenu />
|
||||
</>
|
||||
|
@ -8,6 +8,7 @@ import { EntityTableEffect } from '@/ui/table/components/EntityTableEffect';
|
||||
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { filtersWhereScopedSelector } from '@/ui/view-bar/states/selectors/filtersWhereScopedSelector';
|
||||
import { sortsOrderByScopedSelector } from '@/ui/view-bar/states/selectors/sortsOrderByScopedSelector';
|
||||
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||
@ -32,10 +33,11 @@ export function CompanyTable() {
|
||||
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||
|
||||
const { handleViewsChange, handleViewSubmit } = useTableViews({
|
||||
objectId: 'company',
|
||||
columnDefinitions: companiesAvailableColumnDefinitions,
|
||||
});
|
||||
const { createView, deleteView, submitCurrentView, updateView } =
|
||||
useTableViews({
|
||||
objectId: 'company',
|
||||
columnDefinitions: companiesAvailableColumnDefinitions,
|
||||
});
|
||||
|
||||
const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
|
||||
|
||||
@ -61,27 +63,34 @@ export function CompanyTable() {
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
/>
|
||||
<EntityTable
|
||||
defaultViewName="All Companies"
|
||||
onViewsChange={handleViewsChange}
|
||||
onViewSubmit={handleViewSubmit}
|
||||
onImport={handleImport}
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOneCompanyMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All Companies',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
}}
|
||||
>
|
||||
<EntityTable
|
||||
onImport={handleImport}
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOneCompany) {
|
||||
return;
|
||||
}
|
||||
upsertEntityTableItem(data.updateOneCompany);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
}: {
|
||||
variables: UpdateOneCompanyMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOneCompany) {
|
||||
return;
|
||||
}
|
||||
upsertEntityTableItem(data.updateOneCompany);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { EntityTable } from '@/ui/table/components/EntityTable';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { useUpdateOneCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
import { CompanyTableMockData } from './CompanyTableMockData';
|
||||
@ -7,10 +8,9 @@ export function CompanyTableMockMode() {
|
||||
return (
|
||||
<>
|
||||
<CompanyTableMockData />
|
||||
<EntityTable
|
||||
defaultViewName="All Companies"
|
||||
updateEntityMutation={[useUpdateOneCompanyMutation()]}
|
||||
/>
|
||||
<ViewBarContext.Provider value={{ defaultViewName: 'All Companies' }}>
|
||||
<EntityTable updateEntityMutation={[useUpdateOneCompanyMutation()]} />
|
||||
</ViewBarContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { EntityTableEffect } from '@/ui/table/components/EntityTableEffect';
|
||||
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { filtersWhereScopedSelector } from '@/ui/view-bar/states/selectors/filtersWhereScopedSelector';
|
||||
import { sortsOrderByScopedSelector } from '@/ui/view-bar/states/selectors/sortsOrderByScopedSelector';
|
||||
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||
@ -33,10 +34,11 @@ export function PeopleTable() {
|
||||
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
|
||||
|
||||
const { handleViewsChange, handleViewSubmit } = useTableViews({
|
||||
objectId: 'person',
|
||||
columnDefinitions: peopleAvailableColumnDefinitions,
|
||||
});
|
||||
const { createView, deleteView, submitCurrentView, updateView } =
|
||||
useTableViews({
|
||||
objectId: 'person',
|
||||
columnDefinitions: peopleAvailableColumnDefinitions,
|
||||
});
|
||||
|
||||
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
||||
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
||||
@ -60,27 +62,34 @@ export function PeopleTable() {
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
sortDefinitionArray={peopleAvailableSorts}
|
||||
/>
|
||||
<EntityTable
|
||||
defaultViewName="All People"
|
||||
onViewsChange={handleViewsChange}
|
||||
onViewSubmit={handleViewSubmit}
|
||||
onImport={handleImport}
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOnePersonMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All People',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
}}
|
||||
>
|
||||
<EntityTable
|
||||
onImport={handleImport}
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOnePerson) {
|
||||
return;
|
||||
}
|
||||
upsertEntityTableItem(data.updateOnePerson);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
}: {
|
||||
variables: UpdateOnePersonMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOnePerson) {
|
||||
return;
|
||||
}
|
||||
upsertEntityTableItem(data.updateOnePerson);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,46 +1,101 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
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 { ViewBar, type ViewBarProps } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector';
|
||||
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
|
||||
|
||||
import { BoardOptionsDropdown } from './BoardOptionsDropdown';
|
||||
|
||||
export type BoardHeaderProps = ComponentProps<'div'> & {
|
||||
export type BoardHeaderProps = {
|
||||
className?: string;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
} & Pick<
|
||||
ViewBarProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSubmit' | 'scopeContext'
|
||||
>;
|
||||
} & Pick<ViewBarProps, 'scopeContext'>;
|
||||
|
||||
export function BoardHeader({
|
||||
className,
|
||||
onStageAdd,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
defaultViewName,
|
||||
}: BoardHeaderProps) {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const canPersistBoardCardFields = useRecoilValue(
|
||||
canPersistBoardCardFieldsScopedFamilySelector([
|
||||
tableScopeId,
|
||||
currentViewId,
|
||||
]),
|
||||
);
|
||||
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [savedBoardCardFields, setSavedBoardCardFields] = useRecoilState(
|
||||
savedBoardCardFieldsFamilyState(currentViewId),
|
||||
);
|
||||
|
||||
const handleViewBarReset = () => setBoardCardFields(savedBoardCardFields);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const savedBoardCardFields = await snapshot.getPromise(
|
||||
savedBoardCardFieldsFamilyState(viewId),
|
||||
);
|
||||
set(boardCardFieldsScopedState(tableScopeId), savedBoardCardFields);
|
||||
},
|
||||
[tableScopeId],
|
||||
);
|
||||
|
||||
const handleCurrentViewSubmit = async () => {
|
||||
if (canPersistBoardCardFields) {
|
||||
setSavedBoardCardFields(boardCardFields);
|
||||
}
|
||||
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||
<ViewBar
|
||||
defaultViewName={defaultViewName}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
onViewsChange={onViewsChange}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistBoardCardFields,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
className={className}
|
||||
optionsDropdownButton={
|
||||
<BoardOptionsDropdown
|
||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
|
@ -10,20 +10,22 @@ import {
|
||||
|
||||
type BoardOptionsDropdownProps = Pick<
|
||||
BoardOptionsDropdownContentProps,
|
||||
'customHotkeyScope' | 'onStageAdd' | 'onViewsChange' | 'scopeContext'
|
||||
'customHotkeyScope' | 'onStageAdd' | 'scopeContext'
|
||||
>;
|
||||
|
||||
export function BoardOptionsDropdown({
|
||||
customHotkeyScope,
|
||||
...props
|
||||
onStageAdd,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownProps) {
|
||||
return (
|
||||
<DropdownButton
|
||||
buttonComponents={<BoardOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<BoardOptionsDropdownContent
|
||||
{...props}
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { type Context, useRef, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -23,15 +23,17 @@ import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
@ -40,7 +42,6 @@ import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
@ -60,10 +61,10 @@ type ColumnForCreate = {
|
||||
export function BoardOptionsDropdownContent({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
}: BoardOptionsDropdownContentProps) {
|
||||
const theme = useTheme();
|
||||
const scopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
@ -106,15 +107,24 @@ export function BoardOptionsDropdownContent({
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const { upsertView } = useUpsertView({
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
});
|
||||
const { upsertView } = useUpsertView({ scopeContext });
|
||||
|
||||
const handleViewNameSubmit = async () => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
await upsertView(name);
|
||||
};
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const boardCardFields = await snapshot.getPromise(
|
||||
boardCardFieldsScopedState(scopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
if (view && isCreateMode) {
|
||||
set(savedBoardCardFieldsFamilyState(view.id), boardCardFields);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
|
||||
|
@ -6,10 +6,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import {
|
||||
BoardHeader,
|
||||
BoardHeaderProps,
|
||||
} from '@/ui/board/components/BoardHeader';
|
||||
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
||||
import { StyledBoard } from '@/ui/board/components/StyledBoard';
|
||||
import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
@ -39,10 +36,7 @@ export type EntityBoardProps = {
|
||||
onColumnDelete?: (boardColumnId: string) => void;
|
||||
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
|
||||
scopeContext: Context<string | null>;
|
||||
} & Pick<
|
||||
BoardHeaderProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSubmit'
|
||||
>;
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -57,12 +51,9 @@ const StyledBoardHeader = styled(BoardHeader)`
|
||||
|
||||
export function EntityBoard({
|
||||
boardOptions,
|
||||
defaultViewName,
|
||||
onColumnAdd,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
}: EntityBoardProps) {
|
||||
const [boardColumns] = useRecoilState(boardColumnsState);
|
||||
@ -139,13 +130,7 @@ export function EntityBoard({
|
||||
|
||||
return (boardColumns?.length ?? 0) > 0 ? (
|
||||
<StyledWrapper>
|
||||
<StyledBoardHeader
|
||||
defaultViewName={defaultViewName}
|
||||
onStageAdd={onColumnAdd}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
<StyledBoardHeader onStageAdd={onColumnAdd} scopeContext={scopeContext} />
|
||||
<ScrollWrapper>
|
||||
<StyledBoard ref={boardRef}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
export const savedBoardCardFieldsFamilyState = atomFamily<
|
||||
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
string | undefined
|
||||
>({
|
||||
key: 'savedBoardCardFieldsFamilyState',
|
||||
default: [],
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
||||
|
||||
export const canPersistBoardCardFieldsScopedFamilySelector = selectorFamily({
|
||||
key: 'canPersistBoardCardFieldsScopedFamilySelector',
|
||||
get:
|
||||
([scopeId, viewId]: [string, string | undefined]) =>
|
||||
({ get }) =>
|
||||
!isDeeplyEqual(
|
||||
get(savedBoardCardFieldsFamilyState(viewId)),
|
||||
get(boardCardFieldsScopedState(scopeId)),
|
||||
),
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
|
||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
||||
|
||||
export const savedBoardCardFieldsByKeyFamilySelector = selectorFamily({
|
||||
key: 'savedBoardCardFieldsByKeyFamilySelector',
|
||||
get:
|
||||
(viewId: string | undefined) =>
|
||||
({ get }) =>
|
||||
get(savedBoardCardFieldsFamilyState(viewId)).reduce<
|
||||
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||
>((result, field) => ({ ...result, [field.key]: field }), {}),
|
||||
});
|
@ -87,18 +87,9 @@ const StyledTableContainer = styled.div`
|
||||
|
||||
type OwnProps = {
|
||||
updateEntityMutation: any;
|
||||
} & Pick<
|
||||
TableHeaderProps,
|
||||
'defaultViewName' | 'onImport' | 'onViewsChange' | 'onViewSubmit'
|
||||
>;
|
||||
} & Pick<TableHeaderProps, 'onImport'>;
|
||||
|
||||
export function EntityTable({
|
||||
defaultViewName,
|
||||
onImport,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
updateEntityMutation,
|
||||
}: OwnProps) {
|
||||
export function EntityTable({ onImport, updateEntityMutation }: OwnProps) {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const setRowSelectedState = useSetRowSelectedState();
|
||||
@ -135,12 +126,7 @@ export function EntityTable({
|
||||
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer ref={tableBodyRef}>
|
||||
<TableHeader
|
||||
defaultViewName={defaultViewName}
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSubmit={onViewSubmit}
|
||||
/>
|
||||
<TableHeader onImport={onImport} />
|
||||
<ScrollWrapper>
|
||||
<div>
|
||||
<StyledTable className="entity-table-cell">
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
|
||||
@ -8,13 +7,11 @@ import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
|
||||
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
|
||||
|
||||
type TableOptionsDropdownProps = {
|
||||
onViewsChange?: (views: View[]) => void;
|
||||
onImport?: () => void;
|
||||
customHotkeyScope: HotkeyScope;
|
||||
};
|
||||
|
||||
export function TableOptionsDropdown({
|
||||
onViewsChange,
|
||||
onImport,
|
||||
customHotkeyScope,
|
||||
}: TableOptionsDropdownProps) {
|
||||
@ -23,12 +20,7 @@ export function TableOptionsDropdown({
|
||||
buttonComponents={<TableOptionsDropdownButton />}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownId={TableOptionsDropdownId}
|
||||
dropdownComponents={
|
||||
<TableOptionsDropdownContent
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={<TableOptionsDropdownContent onImport={onImport} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader';
|
||||
@ -11,31 +11,33 @@ import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/icon';
|
||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
|
||||
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
type TableOptionsDropdownButtonProps = {
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
onImport?: () => void;
|
||||
};
|
||||
|
||||
type TableOptionsMenu = 'fields';
|
||||
|
||||
export function TableOptionsDropdownContent({
|
||||
onViewsChange,
|
||||
onImport,
|
||||
}: TableOptionsDropdownButtonProps) {
|
||||
const scopeId = useContextScopeId(TableRecoilScopeContext);
|
||||
|
||||
const { closeDropdownButton } = useDropdownButton({
|
||||
dropdownId: TableOptionsDropdownId,
|
||||
});
|
||||
@ -60,17 +62,28 @@ export function TableOptionsDropdownContent({
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const { handleColumnVisibilityChange } = useTableColumns();
|
||||
|
||||
const { upsertView } = useUpsertView({
|
||||
onViewsChange,
|
||||
scopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
|
||||
const { handleColumnVisibilityChange } = useTableColumns();
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const tableColumns = await snapshot.getPromise(
|
||||
tableColumnsScopedState(scopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
const handleViewNameSubmit = async () => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
await upsertView(name);
|
||||
};
|
||||
if (view && isCreateMode) {
|
||||
set(savedTableColumnsFamilyState(view.id), tableColumns);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const handleSelectMenu = (option: TableOptionsMenu) => {
|
||||
handleViewNameSubmit();
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||
@ -5,7 +6,8 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'
|
||||
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 { ViewBar, ViewBarProps } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBar } from '@/ui/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
@ -18,14 +20,11 @@ import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
export type TableHeaderProps = {
|
||||
onImport?: () => void;
|
||||
} & Pick<ViewBarProps, 'defaultViewName' | 'onViewsChange' | 'onViewSubmit'>;
|
||||
};
|
||||
|
||||
export function TableHeader({
|
||||
onImport,
|
||||
onViewsChange,
|
||||
onViewSubmit,
|
||||
...props
|
||||
}: TableHeaderProps) {
|
||||
export function TableHeader({ onImport }: TableHeaderProps) {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
@ -43,9 +42,7 @@ export function TableHeader({
|
||||
savedTableColumnsFamilyState(currentViewId),
|
||||
);
|
||||
|
||||
function handleViewBarReset() {
|
||||
setTableColumns(savedTableColumns);
|
||||
}
|
||||
const handleViewBarReset = () => setTableColumns(savedTableColumns);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
@ -58,32 +55,36 @@ export function TableHeader({
|
||||
[tableScopeId],
|
||||
);
|
||||
|
||||
async function handleViewSubmit() {
|
||||
async function handleCurrentViewSubmit() {
|
||||
if (canPersistTableColumns) {
|
||||
setSavedTableColumns(tableColumns);
|
||||
}
|
||||
|
||||
await onViewSubmit?.();
|
||||
await onCurrentViewSubmit?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||
<ViewBar
|
||||
{...props}
|
||||
canPersistViewFields={canPersistTableColumns}
|
||||
onReset={handleViewBarReset}
|
||||
onViewSelect={handleViewSelect}
|
||||
onViewSubmit={handleViewSubmit}
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
onImport={onImport}
|
||||
onViewsChange={onViewsChange}
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={TableOptionsDropdownId}
|
||||
scopeContext={TableRecoilScopeContext}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
canPersistViewFields: canPersistTableColumns,
|
||||
onCurrentViewSubmit: handleCurrentViewSubmit,
|
||||
onViewBarReset: handleViewBarReset,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
onImport={onImport}
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownKey={TableOptionsDropdownId}
|
||||
scopeContext={TableRecoilScopeContext}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type OwnProps = ComponentProps<'div'> & {
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
leftComponent?: ReactNode;
|
||||
rightComponent?: ReactNode;
|
||||
bottomComponent?: ReactNode;
|
||||
@ -40,14 +41,14 @@ const StyledRightSection = styled.div`
|
||||
`;
|
||||
|
||||
export function TopBar({
|
||||
className,
|
||||
leftComponent,
|
||||
rightComponent,
|
||||
bottomComponent,
|
||||
displayBottomBorder = true,
|
||||
...props
|
||||
}: OwnProps) {
|
||||
return (
|
||||
<StyledContainer {...props}>
|
||||
<StyledContainer className={className}>
|
||||
<StyledTopBar displayBottomBorder={displayBottomBorder}>
|
||||
<StyledLeftSection>{leftComponent}</StyledLeftSection>
|
||||
<StyledRightSection>{rightComponent}</StyledRightSection>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { type Context, useCallback, useState } from 'react';
|
||||
import { type Context, useCallback, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
@ -21,6 +21,8 @@ import { canPersistSortsScopedFamilySelector } from '@/ui/view-bar/states/select
|
||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: inline-flex;
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
@ -28,22 +30,20 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export type UpdateViewButtonGroupProps = {
|
||||
canPersistViewFields?: boolean;
|
||||
hotkeyScope: string;
|
||||
onViewEditModeChange?: () => void;
|
||||
onViewSubmit?: () => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
export const UpdateViewButtonGroup = ({
|
||||
canPersistViewFields,
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
onViewSubmit,
|
||||
scopeContext,
|
||||
}: UpdateViewButtonGroupProps) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
||||
const { canPersistViewFields, onCurrentViewSubmit } =
|
||||
useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
@ -89,7 +89,7 @@ export const UpdateViewButtonGroup = ({
|
||||
if (canPersistFilters) setSavedFilters(filters);
|
||||
if (canPersistSorts) setSavedSorts(sorts);
|
||||
|
||||
await onViewSubmit?.();
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { ComponentProps, Context, ReactNode } from 'react';
|
||||
import type { Context, ReactNode } from 'react';
|
||||
|
||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||
import { TopBar } from '@/ui/top-bar/TopBar';
|
||||
@ -8,38 +8,22 @@ import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||
|
||||
import { FilterDropdownButton } from './FilterDropdownButton';
|
||||
import { SortDropdownButton } from './SortDropdownButton';
|
||||
import {
|
||||
UpdateViewButtonGroup,
|
||||
type UpdateViewButtonGroupProps,
|
||||
} from './UpdateViewButtonGroup';
|
||||
import ViewBarDetails, { type ViewBarDetailsProps } from './ViewBarDetails';
|
||||
import {
|
||||
ViewsDropdownButton,
|
||||
type ViewsDropdownButtonProps,
|
||||
} from './ViewsDropdownButton';
|
||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||
import ViewBarDetails from './ViewBarDetails';
|
||||
import { ViewsDropdownButton } from './ViewsDropdownButton';
|
||||
|
||||
export type ViewBarProps = ComponentProps<'div'> & {
|
||||
export type ViewBarProps = {
|
||||
className?: string;
|
||||
optionsDropdownButton: ReactNode;
|
||||
optionsDropdownKey: string;
|
||||
scopeContext: Context<string | null>;
|
||||
} & Pick<
|
||||
ViewsDropdownButtonProps,
|
||||
'defaultViewName' | 'onViewsChange' | 'onViewSelect'
|
||||
> &
|
||||
Pick<ViewBarDetailsProps, 'canPersistViewFields' | 'onReset'> &
|
||||
Pick<UpdateViewButtonGroupProps, 'onViewSubmit'>;
|
||||
};
|
||||
|
||||
export const ViewBar = ({
|
||||
canPersistViewFields,
|
||||
defaultViewName,
|
||||
onReset,
|
||||
onViewsChange,
|
||||
onViewSelect,
|
||||
onViewSubmit,
|
||||
className,
|
||||
optionsDropdownButton,
|
||||
optionsDropdownKey,
|
||||
scopeContext,
|
||||
...props
|
||||
}: ViewBarProps) => {
|
||||
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
|
||||
dropdownId: optionsDropdownKey,
|
||||
@ -47,13 +31,10 @@ export const ViewBar = ({
|
||||
|
||||
return (
|
||||
<TopBar
|
||||
{...props}
|
||||
className={className}
|
||||
leftComponent={
|
||||
<ViewsDropdownButton
|
||||
defaultViewName={defaultViewName}
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
onViewsChange={onViewsChange}
|
||||
onViewSelect={onViewSelect}
|
||||
hotkeyScope={ViewsHotkeyScope.ListDropdown}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
@ -75,15 +56,11 @@ export const ViewBar = ({
|
||||
}
|
||||
bottomComponent={
|
||||
<ViewBarDetails
|
||||
canPersistViewFields={canPersistViewFields}
|
||||
context={scopeContext}
|
||||
hasFilterButton
|
||||
onReset={onReset}
|
||||
rightComponent={
|
||||
<UpdateViewButtonGroup
|
||||
canPersistViewFields={canPersistViewFields}
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
onViewSubmit={onViewSubmit}
|
||||
hotkeyScope={ViewsHotkeyScope.CreateDropdown}
|
||||
scopeContext={scopeContext}
|
||||
/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Context, ReactNode } from 'react';
|
||||
import { type Context, type ReactNode, useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
@ -7,6 +7,7 @@ import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextS
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveFilter } from '../hooks/useRemoveFilter';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
@ -23,10 +24,8 @@ import { AddFilterFromDropdownButton } from './AddFilterFromDetailsButton';
|
||||
import SortOrFilterChip from './SortOrFilterChip';
|
||||
|
||||
export type ViewBarDetailsProps = {
|
||||
canPersistViewFields?: boolean;
|
||||
context: Context<string | null>;
|
||||
hasFilterButton?: boolean;
|
||||
onReset?: () => void;
|
||||
rightComponent?: ReactNode;
|
||||
};
|
||||
|
||||
@ -99,12 +98,11 @@ const StyledAddFilterContainer = styled.div`
|
||||
`;
|
||||
|
||||
function ViewBarDetails({
|
||||
canPersistViewFields,
|
||||
context,
|
||||
hasFilterButton = false,
|
||||
onReset,
|
||||
rightComponent,
|
||||
}: ViewBarDetailsProps) {
|
||||
const { canPersistViewFields, onViewBarReset } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(context);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(currentViewIdScopedState, context);
|
||||
@ -155,7 +153,7 @@ function ViewBarDetails({
|
||||
|
||||
const removeFilter = useRemoveFilter(context);
|
||||
function handleCancelClick() {
|
||||
onReset?.();
|
||||
onViewBarReset?.();
|
||||
setFilters(savedFilters);
|
||||
setSorts(savedSorts);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {
|
||||
type Context,
|
||||
type MouseEvent,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
@ -22,7 +23,6 @@ import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
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 DropdownButton from '@/ui/view-bar/components/DropdownButton';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
@ -33,10 +33,12 @@ import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/curren
|
||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { useRemoveView } from '../hooks/useRemoveView';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(
|
||||
StyledDropdownMenuItemsContainer,
|
||||
)`
|
||||
@ -71,39 +73,28 @@ const StyledViewName = styled.span`
|
||||
`;
|
||||
|
||||
export type ViewsDropdownButtonProps = {
|
||||
defaultViewName: string;
|
||||
hotkeyScope: ViewsHotkeyScope;
|
||||
onViewEditModeChange?: () => void;
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
onViewSelect?: (viewId: string) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
};
|
||||
|
||||
export const ViewsDropdownButton = ({
|
||||
defaultViewName,
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
onViewsChange,
|
||||
onViewSelect,
|
||||
scopeContext,
|
||||
}: ViewsDropdownButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
const { defaultViewName, onViewSelect } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const [, setCurrentViewId] = useRecoilScopedState(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [isUnfolded, setIsUnfolded] = useState(false);
|
||||
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
scopeContext,
|
||||
);
|
||||
const [views, setViews] = useRecoilScopedState(
|
||||
viewsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const views = useRecoilScopedValue(viewsScopedState, scopeContext);
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
const {
|
||||
@ -146,20 +137,17 @@ export const ViewsDropdownButton = ({
|
||||
[setViewEditMode, onViewEditModeChange],
|
||||
);
|
||||
|
||||
const handleDeleteViewButtonClick = useCallback(
|
||||
async (event: MouseEvent<HTMLButtonElement>, viewId: string) => {
|
||||
event.stopPropagation();
|
||||
const { removeView } = useRemoveView({ scopeContext });
|
||||
|
||||
if (currentView?.id === viewId) setCurrentViewId(undefined);
|
||||
const handleDeleteViewButtonClick = async (
|
||||
event: MouseEvent<HTMLButtonElement>,
|
||||
viewId: string,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const nextViews = views.filter((view) => view.id !== viewId);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
setIsUnfolded(false);
|
||||
},
|
||||
[currentView?.id, onViewsChange, setCurrentViewId, setViews, views],
|
||||
);
|
||||
await removeView(viewId);
|
||||
setIsUnfolded(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
isUnfolded
|
||||
|
14
front/src/modules/ui/view-bar/contexts/ViewBarContext.ts
Normal file
14
front/src/modules/ui/view-bar/contexts/ViewBarContext.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { View } from '../types/View';
|
||||
|
||||
export const ViewBarContext = createContext<{
|
||||
canPersistViewFields?: boolean;
|
||||
defaultViewName?: string;
|
||||
onCurrentViewSubmit?: () => void | Promise<void>;
|
||||
onViewBarReset?: () => void;
|
||||
onViewCreate?: (view: View) => void | Promise<void>;
|
||||
onViewEdit?: (view: View) => void | Promise<void>;
|
||||
onViewRemove?: (viewId: string) => void | Promise<void>;
|
||||
onViewSelect?: (viewId: string) => void | Promise<void>;
|
||||
}>({});
|
37
front/src/modules/ui/view-bar/hooks/useRemoveView.ts
Normal file
37
front/src/modules/ui/view-bar/hooks/useRemoveView.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { type Context, useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
|
||||
export const useRemoveView = ({
|
||||
scopeContext,
|
||||
}: {
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const { onViewRemove } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const removeView = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentViewId = await snapshot.getPromise(
|
||||
currentViewIdScopedState(recoilScopeId),
|
||||
);
|
||||
|
||||
if (currentViewId === viewId)
|
||||
set(currentViewIdScopedState(recoilScopeId), undefined);
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.filter((view) => view.id !== viewId),
|
||||
);
|
||||
await onViewRemove?.(viewId);
|
||||
},
|
||||
[onViewRemove, recoilScopeId],
|
||||
);
|
||||
|
||||
return { removeView };
|
||||
};
|
@ -1,37 +1,30 @@
|
||||
import { Context, useCallback } from 'react';
|
||||
import { type Context, useCallback, useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '../states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '../states/savedSortsFamilyState';
|
||||
import { viewsByIdScopedSelector } from '../states/selectors/viewsByIdScopedSelector';
|
||||
import { sortsScopedState } from '../states/sortsScopedState';
|
||||
import { viewEditModeState } from '../states/viewEditModeState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
import type { View } from '../types/View';
|
||||
|
||||
export const useUpsertView = ({
|
||||
onViewsChange,
|
||||
scopeContext,
|
||||
}: {
|
||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const { onViewCreate, onViewEdit } = useContext(ViewBarContext);
|
||||
const recoilScopeId = useContextScopeId(scopeContext);
|
||||
|
||||
const filters = useRecoilScopedValue(filtersScopedState, scopeContext);
|
||||
const sorts = useRecoilScopedValue(sortsScopedState, scopeContext);
|
||||
|
||||
const [, setCurrentViewId] = useRecoilScopedState(
|
||||
currentViewIdScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [views, setViews] = useRecoilScopedState(
|
||||
viewsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const [viewEditMode, setViewEditMode] = useRecoilState(viewEditModeState);
|
||||
|
||||
const resetViewEditMode = useCallback(
|
||||
@ -40,44 +33,63 @@ export const useUpsertView = ({
|
||||
);
|
||||
|
||||
const upsertView = useRecoilCallback(
|
||||
({ set }) =>
|
||||
({ set, snapshot }) =>
|
||||
async (name?: string) => {
|
||||
if (!viewEditMode.mode || !name) return resetViewEditMode();
|
||||
if (!viewEditMode.mode || !name) {
|
||||
resetViewEditMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewEditMode.mode === 'create') {
|
||||
const viewToCreate = { id: v4(), name };
|
||||
const nextViews = [...views, viewToCreate];
|
||||
const createdView = { id: v4(), name };
|
||||
|
||||
set(savedFiltersFamilyState(viewToCreate.id), filters);
|
||||
set(savedSortsFamilyState(viewToCreate.id), sorts);
|
||||
set(savedFiltersFamilyState(createdView.id), filters);
|
||||
set(savedSortsFamilyState(createdView.id), sorts);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) => [
|
||||
...previousViews,
|
||||
createdView,
|
||||
]);
|
||||
|
||||
setCurrentViewId(viewToCreate.id);
|
||||
await onViewCreate?.(createdView);
|
||||
|
||||
resetViewEditMode();
|
||||
|
||||
set(currentViewIdScopedState(recoilScopeId), createdView.id);
|
||||
|
||||
return createdView;
|
||||
}
|
||||
|
||||
if (viewEditMode.mode === 'edit') {
|
||||
const nextViews = views.map((view) =>
|
||||
view.id === viewEditMode.viewId ? { ...view, name } : view,
|
||||
if (viewEditMode.mode === 'edit' && viewEditMode.viewId) {
|
||||
const viewsById = await snapshot.getPromise(
|
||||
viewsByIdScopedSelector(recoilScopeId),
|
||||
);
|
||||
const editedView = { ...viewsById[viewEditMode.viewId], name };
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.map((previousView) =>
|
||||
previousView.id === viewEditMode.viewId
|
||||
? editedView
|
||||
: previousView,
|
||||
),
|
||||
);
|
||||
|
||||
setViews(nextViews);
|
||||
await onViewsChange?.(nextViews);
|
||||
}
|
||||
await onViewEdit?.(editedView);
|
||||
|
||||
return resetViewEditMode();
|
||||
resetViewEditMode();
|
||||
|
||||
return editedView;
|
||||
}
|
||||
},
|
||||
[
|
||||
filters,
|
||||
onViewsChange,
|
||||
onViewCreate,
|
||||
onViewEdit,
|
||||
recoilScopeId,
|
||||
resetViewEditMode,
|
||||
setCurrentViewId,
|
||||
setViews,
|
||||
sorts,
|
||||
viewEditMode.mode,
|
||||
viewEditMode.viewId,
|
||||
views,
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { type Context } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { availableBoardCardFieldsScopedState } from '@/ui/board/states/availableBoardCardFieldsScopedState';
|
||||
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
|
||||
import { savedBoardCardFieldsFamilyState } from '@/ui/board/states/savedBoardCardFieldsFamilyState';
|
||||
import { savedBoardCardFieldsByKeyFamilySelector } from '@/ui/board/states/selectors/savedBoardCardFieldsByKeyFamilySelector';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
@ -13,6 +16,7 @@ import {
|
||||
SortOrder,
|
||||
useCreateViewFieldsMutation,
|
||||
useGetViewFieldsQuery,
|
||||
useUpdateViewFieldMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
@ -49,8 +53,15 @@ export const useBoardViewFields = ({
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const setSavedBoardCardFields = useSetRecoilState(
|
||||
savedBoardCardFieldsFamilyState(currentViewId),
|
||||
);
|
||||
const savedBoardCardFieldsByKey = useRecoilValue(
|
||||
savedBoardCardFieldsByKeyFamilySelector(currentViewId),
|
||||
);
|
||||
|
||||
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
|
||||
const [updateViewFieldMutation] = useUpdateViewFieldMutation();
|
||||
|
||||
const createViewFields = (
|
||||
fields: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
@ -68,6 +79,27 @@ export const useBoardViewFields = ({
|
||||
});
|
||||
};
|
||||
|
||||
const updateViewFields = (
|
||||
fields: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
) => {
|
||||
if (!currentViewId || !fields.length) return;
|
||||
|
||||
return Promise.all(
|
||||
fields.map((field) =>
|
||||
updateViewFieldMutation({
|
||||
variables: {
|
||||
data: {
|
||||
isVisible: field.isVisible,
|
||||
},
|
||||
where: {
|
||||
viewId_key: { key: field.key, viewId: currentViewId },
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const { refetch } = useGetViewFieldsQuery({
|
||||
skip: !currentViewId || skipFetch,
|
||||
variables: {
|
||||
@ -102,6 +134,7 @@ export const useBoardViewFields = ({
|
||||
.filter<ViewFieldDefinition<ViewFieldMetadata>>(assertNotNull);
|
||||
|
||||
if (!isDeeplyEqual(boardCardFields, nextFields)) {
|
||||
setSavedBoardCardFields(nextFields);
|
||||
setBoardCardFields(nextFields);
|
||||
}
|
||||
|
||||
@ -110,4 +143,24 @@ export const useBoardViewFields = ({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const persistCardFields = async () => {
|
||||
if (!currentViewId) return;
|
||||
|
||||
const viewFieldsToCreate = boardCardFields.filter(
|
||||
(field) => !savedBoardCardFieldsByKey[field.key],
|
||||
);
|
||||
await createViewFields(viewFieldsToCreate);
|
||||
|
||||
const viewFieldsToUpdate = boardCardFields.filter(
|
||||
(field) =>
|
||||
savedBoardCardFieldsByKey[field.key] &&
|
||||
savedBoardCardFieldsByKey[field.key].isVisible !== field.isVisible,
|
||||
);
|
||||
await updateViewFields(viewFieldsToUpdate);
|
||||
|
||||
return refetch();
|
||||
};
|
||||
|
||||
return { createViewFields, persistCardFields };
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { Context } from 'react';
|
||||
|
||||
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
@ -23,17 +24,21 @@ export const useBoardViews = ({
|
||||
objectId: 'company';
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const boardCardFields = useRecoilScopedValue(
|
||||
boardCardFieldsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const filters = useRecoilScopedValue(filtersScopedState, scopeContext);
|
||||
const sorts = useRecoilScopedValue(sortsScopedState, scopeContext);
|
||||
|
||||
const { handleViewsChange, isFetchingViews } = useViews({
|
||||
const { createView, deleteView, isFetchingViews, updateView } = useViews({
|
||||
objectId,
|
||||
onViewCreate: handleViewCreate,
|
||||
type: ViewType.Pipeline,
|
||||
scopeContext,
|
||||
});
|
||||
|
||||
useBoardViewFields({
|
||||
const { createViewFields, persistCardFields } = useBoardViewFields({
|
||||
objectId,
|
||||
fieldDefinitions,
|
||||
scopeContext,
|
||||
@ -51,14 +56,16 @@ export const useBoardViews = ({
|
||||
});
|
||||
|
||||
async function handleViewCreate(viewId: string) {
|
||||
await createViewFields(boardCardFields, viewId);
|
||||
await createViewFilters(filters, viewId);
|
||||
await createViewSorts(sorts, viewId);
|
||||
}
|
||||
|
||||
const handleViewSubmit = async () => {
|
||||
const submitCurrentView = async () => {
|
||||
await persistCardFields();
|
||||
await persistFilters();
|
||||
await persistSorts();
|
||||
};
|
||||
|
||||
return { handleViewsChange, handleViewSubmit };
|
||||
return { createView, deleteView, submitCurrentView, updateView };
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ export const useTableViews = ({
|
||||
);
|
||||
const sorts = useRecoilScopedValue(sortsScopedState, TableRecoilScopeContext);
|
||||
|
||||
const { handleViewsChange, isFetchingViews } = useViews({
|
||||
const { createView, deleteView, isFetchingViews, updateView } = useViews({
|
||||
objectId,
|
||||
onViewCreate: handleViewCreate,
|
||||
type: ViewType.Table,
|
||||
@ -55,11 +55,11 @@ export const useTableViews = ({
|
||||
await createViewSorts(sorts, viewId);
|
||||
}
|
||||
|
||||
const handleViewSubmit = async () => {
|
||||
const submitCurrentView = async () => {
|
||||
await persistColumns();
|
||||
await persistFilters();
|
||||
await persistSorts();
|
||||
};
|
||||
|
||||
return { handleViewsChange, handleViewSubmit };
|
||||
return { createView, deleteView, submitCurrentView, updateView };
|
||||
};
|
||||
|
@ -1,13 +1,13 @@
|
||||
import type { Context } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { savedBoardCardFieldsFamilyState } from '@/ui/board/states/savedBoardCardFieldsFamilyState';
|
||||
import { savedTableColumnsFamilyState } from '@/ui/table/states/savedTableColumnsFamilyState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '@/ui/view-bar/states/savedSortsFamilyState';
|
||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
|
||||
import type { View } from '@/ui/view-bar/types/View';
|
||||
import {
|
||||
@ -19,6 +19,8 @@ import {
|
||||
} from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { GET_VIEWS } from '../graphql/queries/getViews';
|
||||
|
||||
export const useViews = ({
|
||||
objectId,
|
||||
onViewCreate,
|
||||
@ -38,7 +40,6 @@ export const useViews = ({
|
||||
viewsScopedState,
|
||||
scopeContext,
|
||||
);
|
||||
const viewsById = useRecoilScopedValue(viewsByIdScopedSelector, scopeContext);
|
||||
|
||||
const [createViewMutation] = useCreateViewMutation();
|
||||
const [updateViewMutation] = useUpdateViewMutation();
|
||||
@ -53,26 +54,34 @@ export const useViews = ({
|
||||
type,
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
|
||||
});
|
||||
|
||||
if (data?.view) await onViewCreate?.(data.view.id);
|
||||
};
|
||||
|
||||
const updateView = (view: View) =>
|
||||
updateViewMutation({
|
||||
const updateView = async (view: View) => {
|
||||
await updateViewMutation({
|
||||
variables: {
|
||||
data: { name: view.name },
|
||||
where: { id: view.id },
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
const deleteView = (viewId: string) =>
|
||||
deleteViewMutation({ variables: { where: { id: viewId } } });
|
||||
const deleteView = async (viewId: string) => {
|
||||
await deleteViewMutation({
|
||||
variables: { where: { id: viewId } },
|
||||
refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
|
||||
});
|
||||
};
|
||||
|
||||
const handleResetSavedViews = useRecoilCallback(
|
||||
({ reset }) =>
|
||||
() => {
|
||||
views.forEach((view) => {
|
||||
reset(savedBoardCardFieldsFamilyState(view.id));
|
||||
reset(savedTableColumnsFamilyState(view.id));
|
||||
reset(savedFiltersFamilyState(view.id));
|
||||
reset(savedSortsFamilyState(view.id));
|
||||
@ -81,7 +90,7 @@ export const useViews = ({
|
||||
[views],
|
||||
);
|
||||
|
||||
const { loading, refetch } = useGetViewsQuery({
|
||||
const { loading } = useGetViewsQuery({
|
||||
variables: {
|
||||
where: {
|
||||
objectId: { equals: objectId },
|
||||
@ -115,32 +124,10 @@ export const useViews = ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleViewsChange = async (nextViews: View[]) => {
|
||||
const viewToCreate = nextViews.find((nextView) => !viewsById[nextView.id]);
|
||||
if (viewToCreate) {
|
||||
await createView(viewToCreate);
|
||||
await refetch();
|
||||
return;
|
||||
}
|
||||
|
||||
const viewToUpdate = nextViews.find(
|
||||
(nextView) =>
|
||||
viewsById[nextView.id] && viewsById[nextView.id].name !== nextView.name,
|
||||
);
|
||||
if (viewToUpdate) {
|
||||
await updateView(viewToUpdate);
|
||||
await refetch();
|
||||
return;
|
||||
}
|
||||
|
||||
const nextViewIds = nextViews.map((nextView) => nextView.id);
|
||||
const viewIdToDelete = Object.keys(viewsById).find(
|
||||
(previousViewId) => !nextViewIds.includes(previousViewId),
|
||||
);
|
||||
if (viewIdToDelete) await deleteView(viewIdToDelete);
|
||||
|
||||
await refetch();
|
||||
return {
|
||||
createView,
|
||||
deleteView,
|
||||
isFetchingViews: loading,
|
||||
updateView,
|
||||
};
|
||||
|
||||
return { handleViewsChange, isFetchingViews: loading };
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user