import { useCallback } from 'react'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import type { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField'; import { availableTableColumnsScopedState } from '@/ui/table/states/availableTableColumnsScopedState'; import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; import { savedTableColumnsScopedState } from '@/ui/table/states/savedTableColumnsScopedState'; import { savedTableColumnsByKeyScopedSelector } from '@/ui/table/states/selectors/savedTableColumnsByKeyScopedSelector'; import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState'; import { currentTableViewIdState } from '@/ui/table/states/tableViewsState'; import type { ColumnDefinition } from '@/ui/table/types/ColumnDefinition'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { SortOrder, useCreateViewFieldsMutation, useGetViewFieldsQuery, useUpdateViewFieldMutation, } from '~/generated/graphql'; import { assertNotNull } from '~/utils/assert'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; const toViewFieldInput = ( objectId: 'company' | 'person', fieldDefinition: ColumnDefinition, ) => ({ key: fieldDefinition.key, name: fieldDefinition.name, index: fieldDefinition.index, isVisible: fieldDefinition.isVisible ?? true, objectId, size: fieldDefinition.size, }); export const useTableViewFields = ({ objectId, columnDefinitions, skipFetch, }: { objectId: 'company' | 'person'; columnDefinitions: ColumnDefinition[]; skipFetch?: boolean; }) => { const currentTableViewId = useRecoilScopedValue( currentTableViewIdState, TableRecoilScopeContext, ); const [availableTableColumns, setAvailableTableColumns] = useRecoilScopedState( availableTableColumnsScopedState, TableRecoilScopeContext, ); const [tableColumns, setTableColumns] = useRecoilScopedState( tableColumnsScopedState, TableRecoilScopeContext, ); const setSavedTableColumns = useSetRecoilState( savedTableColumnsScopedState(currentTableViewId), ); const savedTableColumnsByKey = useRecoilValue( savedTableColumnsByKeyScopedSelector(currentTableViewId), ); const [createViewFieldsMutation] = useCreateViewFieldsMutation(); const [updateViewFieldMutation] = useUpdateViewFieldMutation(); const createViewFields = useCallback( ( columns: ColumnDefinition[], viewId = currentTableViewId, ) => { if (!viewId || !columns.length) return; return createViewFieldsMutation({ variables: { data: columns.map((column) => ({ ...toViewFieldInput(objectId, column), viewId, })), }, }); }, [createViewFieldsMutation, currentTableViewId, objectId], ); const updateViewFields = useCallback( (columns: ColumnDefinition[]) => { if (!currentTableViewId || !columns.length) return; return Promise.all( columns.map((column) => updateViewFieldMutation({ variables: { data: { isVisible: column.isVisible, size: column.size, }, where: { viewId_key: { key: column.key, viewId: currentTableViewId }, }, }, }), ), ); }, [currentTableViewId, updateViewFieldMutation], ); const { refetch } = useGetViewFieldsQuery({ skip: !currentTableViewId || skipFetch, variables: { orderBy: { index: SortOrder.Asc }, where: { viewId: { equals: currentTableViewId }, }, }, onCompleted: async (data) => { if (!data.viewFields.length) { // Populate if empty await createViewFields(columnDefinitions); return refetch(); } const nextColumns = data.viewFields .map | null>((viewField) => { const columnDefinition = columnDefinitions.find( ({ key }) => viewField.key === key, ); return columnDefinition ? { ...columnDefinition, key: viewField.key, name: viewField.name, index: viewField.index, size: viewField.size ?? columnDefinition.size, isVisible: viewField.isVisible, } : null; }) .filter>(assertNotNull); if (!isDeeplyEqual(tableColumns, nextColumns)) { setSavedTableColumns(nextColumns); setTableColumns(nextColumns); } if (!availableTableColumns.length) { setAvailableTableColumns(columnDefinitions); } }, }); const persistColumns = useCallback(async () => { if (!currentTableViewId) return; const viewFieldsToCreate = tableColumns.filter( (column) => !savedTableColumnsByKey[column.key], ); await createViewFields(viewFieldsToCreate); const viewFieldsToUpdate = tableColumns.filter( (column) => savedTableColumnsByKey[column.key] && (savedTableColumnsByKey[column.key].size !== column.size || savedTableColumnsByKey[column.key].isVisible !== column.isVisible), ); await updateViewFields(viewFieldsToUpdate); return refetch(); }, [ createViewFields, currentTableViewId, refetch, savedTableColumnsByKey, tableColumns, updateViewFields, ]); return { createViewFields, persistColumns }; };