@ -2,27 +2,16 @@ import { SortOrder as Order_By } from '~/generated/graphql';
|
||||
|
||||
import { SelectedSortType } from './types/interface';
|
||||
|
||||
const mapOrderToOrder_By = (order: string) => {
|
||||
if (order === 'asc') return Order_By.Asc;
|
||||
return Order_By.Desc;
|
||||
};
|
||||
|
||||
export const defaultOrderByTemplateFactory =
|
||||
(key: string) => (order: string) => ({
|
||||
[key]: order,
|
||||
});
|
||||
|
||||
export const reduceSortsToOrderBy = <OrderByTemplate>(
|
||||
sorts: Array<SelectedSortType<OrderByTemplate>>,
|
||||
): OrderByTemplate[] => {
|
||||
const mappedSorts = sorts.map((sort) => {
|
||||
if (sort.orderByTemplates) {
|
||||
return sort.orderByTemplates?.map((orderByTemplate) =>
|
||||
orderByTemplate(mapOrderToOrder_By(sort.order)),
|
||||
sorts: SelectedSortType<OrderByTemplate>[],
|
||||
): OrderByTemplate[] =>
|
||||
sorts
|
||||
.map((sort) => {
|
||||
const order = sort.order === 'asc' ? Order_By.Asc : Order_By.Desc;
|
||||
return (
|
||||
sort.orderByTemplate?.(order) || [
|
||||
{ [sort.key]: order } as OrderByTemplate,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return defaultOrderByTemplateFactory(sort.key as string)(sort.order);
|
||||
});
|
||||
return mappedSorts.flat() as OrderByTemplate[];
|
||||
};
|
||||
})
|
||||
.flat();
|
||||
|
||||
@ -1,8 +1,28 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
import { atomFamily, selectorFamily } from 'recoil';
|
||||
|
||||
import { Filter } from '../types/Filter';
|
||||
import { reduceSortsToOrderBy } from '../helpers';
|
||||
import { SelectedSortType } from '../types/interface';
|
||||
|
||||
export const sortScopedState = atomFamily<Filter[], string>({
|
||||
export const sortScopedState = atomFamily<SelectedSortType<any>[], string>({
|
||||
key: 'sortScopedState',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const sortsByKeyScopedState = selectorFamily({
|
||||
key: 'sortsByKeyScopedState',
|
||||
get:
|
||||
(param: string) =>
|
||||
({ get }) =>
|
||||
get(sortScopedState(param)).reduce<Record<string, SelectedSortType<any>>>(
|
||||
(result, sort) => ({ ...result, [sort.key]: sort }),
|
||||
{},
|
||||
),
|
||||
});
|
||||
|
||||
export const sortsOrderByScopedState = selectorFamily({
|
||||
key: 'sortsOrderByScopedState',
|
||||
get:
|
||||
(param: string) =>
|
||||
({ get }) =>
|
||||
reduceSortsToOrderBy(get(sortScopedState(param))),
|
||||
});
|
||||
|
||||
@ -6,7 +6,7 @@ export type SortType<OrderByTemplate> = {
|
||||
label: string;
|
||||
key: string;
|
||||
icon?: ReactNode;
|
||||
orderByTemplates?: Array<(order: Order_By) => OrderByTemplate>;
|
||||
orderByTemplate?: (order: Order_By) => OrderByTemplate[];
|
||||
};
|
||||
|
||||
export type SelectedSortType<OrderByTemplate> = SortType<OrderByTemplate> & {
|
||||
|
||||
@ -5,19 +5,20 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { IconButton } from '@/ui/button/components/IconButton';
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { IconPlus } from '@/ui/icon';
|
||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||
import { GET_VIEW_FIELDS } from '@/views/queries/select';
|
||||
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
||||
import {
|
||||
useCreateViewFieldMutation,
|
||||
useUpdateViewFieldMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '../../editable-field/types/ViewField';
|
||||
import { toViewFieldInput } from '../hooks/useLoadView';
|
||||
import { toViewFieldInput } from '../hooks/useLoadViewFields';
|
||||
import { resizeFieldOffsetState } from '../states/resizeFieldOffsetState';
|
||||
import {
|
||||
addableViewFieldDefinitionsState,
|
||||
@ -89,6 +90,7 @@ export function EntityTableHeader() {
|
||||
const theme = useTheme();
|
||||
|
||||
const [{ objectName }, setViewFieldsState] = useRecoilState(viewFieldsState);
|
||||
const currentViewId = useRecoilValue(currentViewIdState);
|
||||
const viewFields = useRecoilValue(visibleViewFieldsState);
|
||||
const columnWidths = useRecoilValue(columnWidthByViewFieldIdState);
|
||||
const addableViewFieldDefinitions = useRecoilValue(
|
||||
@ -176,15 +178,18 @@ export function EntityTableHeader() {
|
||||
|
||||
createViewFieldMutation({
|
||||
variables: {
|
||||
data: toViewFieldInput(objectName, {
|
||||
...viewFieldDefinition,
|
||||
columnOrder: viewFields.length + 1,
|
||||
}),
|
||||
data: {
|
||||
...toViewFieldInput(objectName, {
|
||||
...viewFieldDefinition,
|
||||
columnOrder: viewFields.length + 1,
|
||||
}),
|
||||
view: { connect: { id: currentViewId } },
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
||||
});
|
||||
},
|
||||
[createViewFieldMutation, objectName, viewFields.length],
|
||||
[createViewFieldMutation, currentViewId, objectName, viewFields.length],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
||||
|
||||
import { useLoadView } from '../hooks/useLoadView';
|
||||
import { useLoadViewFields } from '../hooks/useLoadViewFields';
|
||||
|
||||
export function GenericEntityTableData({
|
||||
objectName,
|
||||
@ -27,7 +27,7 @@ export function GenericEntityTableData({
|
||||
}) {
|
||||
const setEntityTableData = useSetEntityTableData();
|
||||
|
||||
useLoadView({ objectName, viewFieldDefinitions });
|
||||
useLoadViewFields({ objectName, viewFieldDefinitions });
|
||||
|
||||
useGetRequest({
|
||||
variables: { orderBy, where: whereFilters },
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldTextMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { GET_VIEW_FIELDS } from '@/views/queries/select';
|
||||
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
||||
import {
|
||||
SortOrder,
|
||||
useCreateViewFieldsMutation,
|
||||
useGetViewFieldsQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldTextMetadata,
|
||||
} from '../../editable-field/types/ViewField';
|
||||
import { entityTableDimensionsState } from '../states/entityTableDimensionsState';
|
||||
import { viewFieldsState } from '../states/viewFieldsState';
|
||||
|
||||
@ -33,13 +34,14 @@ export const toViewFieldInput = (
|
||||
sizeInPx: viewFieldDefinition.columnSize,
|
||||
});
|
||||
|
||||
export const useLoadView = ({
|
||||
export const useLoadViewFields = ({
|
||||
objectName,
|
||||
viewFieldDefinitions,
|
||||
}: {
|
||||
objectName: 'company' | 'person';
|
||||
viewFieldDefinitions: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||
}) => {
|
||||
const currentViewId = useRecoilValue(currentViewIdState);
|
||||
const setEntityTableDimensions = useSetRecoilState(
|
||||
entityTableDimensionsState,
|
||||
);
|
||||
@ -50,7 +52,10 @@ export const useLoadView = ({
|
||||
useGetViewFieldsQuery({
|
||||
variables: {
|
||||
orderBy: { index: SortOrder.Asc },
|
||||
where: { objectName: { equals: objectName } },
|
||||
where: {
|
||||
objectName: { equals: objectName },
|
||||
viewId: { equals: currentViewId ?? null },
|
||||
},
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (data.viewFields.length) {
|
||||
@ -79,9 +84,10 @@ export const useLoadView = ({
|
||||
// Populate if empty
|
||||
createViewFieldsMutation({
|
||||
variables: {
|
||||
data: viewFieldDefinitions.map((viewFieldDefinition) =>
|
||||
toViewFieldInput(objectName, viewFieldDefinition),
|
||||
),
|
||||
data: viewFieldDefinitions.map((viewFieldDefinition) => ({
|
||||
...toViewFieldInput(objectName, viewFieldDefinition),
|
||||
viewId: currentViewId,
|
||||
})),
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
||||
});
|
||||
@ -1,12 +1,14 @@
|
||||
import { ReactNode, useCallback, useState } from 'react';
|
||||
import { ReactNode, useCallback } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { FilterDropdownButton } from '@/ui/filter-n-sort/components/FilterDropdownButton';
|
||||
import SortAndFilterBar from '@/ui/filter-n-sort/components/SortAndFilterBar';
|
||||
import { SortDropdownButton } from '@/ui/filter-n-sort/components/SortDropdownButton';
|
||||
import { sortScopedState } from '@/ui/filter-n-sort/states/sortScopedState';
|
||||
import { FiltersHotkeyScope } from '@/ui/filter-n-sort/types/FiltersHotkeyScope';
|
||||
import { SelectedSortType, SortType } from '@/ui/filter-n-sort/types/interface';
|
||||
import { TopBar } from '@/ui/top-bar/TopBar';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { OptionsDropdownButton } from '@/views/components/OptionsDropdownButton';
|
||||
|
||||
import { TableContext } from '../../states/TableContext';
|
||||
@ -34,26 +36,26 @@ export function TableHeader<SortField>({
|
||||
availableSorts,
|
||||
onSortsUpdate,
|
||||
}: OwnProps<SortField>) {
|
||||
const [sorts, innerSetSorts] = useState<Array<SelectedSortType<SortField>>>(
|
||||
[],
|
||||
const [sorts, setSorts] = useRecoilScopedState<SelectedSortType<SortField>[]>(
|
||||
sortScopedState,
|
||||
TableContext,
|
||||
);
|
||||
const handleSortsUpdate = onSortsUpdate ?? setSorts;
|
||||
|
||||
const sortSelect = useCallback(
|
||||
(newSort: SelectedSortType<SortField>) => {
|
||||
const newSorts = updateSortOrFilterByKey(sorts, newSort);
|
||||
innerSetSorts(newSorts);
|
||||
onSortsUpdate && onSortsUpdate(newSorts);
|
||||
handleSortsUpdate(newSorts);
|
||||
},
|
||||
[onSortsUpdate, sorts],
|
||||
[handleSortsUpdate, sorts],
|
||||
);
|
||||
|
||||
const sortUnselect = useCallback(
|
||||
(sortKey: string) => {
|
||||
const newSorts = sorts.filter((sort) => sort.key !== sortKey);
|
||||
innerSetSorts(newSorts);
|
||||
onSortsUpdate && onSortsUpdate(newSorts);
|
||||
handleSortsUpdate(newSorts);
|
||||
},
|
||||
[onSortsUpdate, sorts],
|
||||
[handleSortsUpdate, sorts],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -88,8 +90,7 @@ export function TableHeader<SortField>({
|
||||
sorts={sorts}
|
||||
onRemoveSort={sortUnselect}
|
||||
onCancelClick={() => {
|
||||
innerSetSorts([]);
|
||||
onSortsUpdate && onSortsUpdate([]);
|
||||
handleSortsUpdate([]);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Context, useContext } from 'react';
|
||||
import { RecoilState, useRecoilValue } from 'recoil';
|
||||
import { RecoilState, RecoilValueReadOnly, useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecoilScopeContext } from '../states/RecoilScopeContext';
|
||||
|
||||
export function useRecoilScopedValue<T>(
|
||||
recoilState: (param: string) => RecoilState<T>,
|
||||
recoilState: (param: string) => RecoilState<T> | RecoilValueReadOnly<T>,
|
||||
SpecificContext?: Context<string | null>,
|
||||
) {
|
||||
const recoilScopeId = useContext(SpecificContext ?? RecoilScopeContext);
|
||||
|
||||
Reference in New Issue
Block a user