Add dnd to calendar view (#14539)
This commit is contained in:
+7
-58
@@ -1,82 +1,31 @@
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarQueryDateRangeFilter } from '@/object-record/record-calendar/month/hooks/useRecordCalendarQueryDateRangeFilter';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { combineFilters } from '@/object-record/record-filter/utils/combineFilters';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { turnAnyFieldFilterIntoRecordGqlFilter } from '@/object-record/record-filter/utils/turnAnyFieldFilterIntoRecordGqlFilter';
|
||||
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
const recordCalendarSelectedDate = useRecoilComponentValue(
|
||||
recordCalendarSelectedDateComponentState,
|
||||
);
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
|
||||
const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
const currentRecordFilterGroups = useRecoilComponentValue(
|
||||
currentRecordFilterGroupsComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useRecoilComponentValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const currentRecordSorts = useRecoilComponentValue(
|
||||
currentRecordSortsComponentState,
|
||||
);
|
||||
|
||||
const setRecordIndexAllRecordIdsSelector = useSetRecoilComponentState(
|
||||
recordIndexAllRecordIdsComponentSelector,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
const objectNameSingular = objectMetadataItem.nameSingular;
|
||||
|
||||
const requestFilters = computeRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilters: currentRecordFilters,
|
||||
recordFilterGroups: currentRecordFilterGroups,
|
||||
fields: objectMetadataItem.fields,
|
||||
});
|
||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValue(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
objectMetadataItem,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const { dateRangeFilter } = useRecordCalendarQueryDateRangeFilter(
|
||||
recordCalendarSelectedDate,
|
||||
);
|
||||
|
||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||
|
||||
const combinedFilters = combineFilters([
|
||||
anyFieldFilter,
|
||||
requestFilters,
|
||||
dateRangeFilter,
|
||||
]);
|
||||
const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem });
|
||||
|
||||
const { records } = useFindManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: combinedFilters,
|
||||
orderBy,
|
||||
...params,
|
||||
limit: 100,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+34
-4
@@ -2,9 +2,19 @@ import { RecordCalendarMonthBody } from '@/object-record/record-calendar/month/c
|
||||
import { RecordCalendarMonthHeader } from '@/object-record/record-calendar/month/components/RecordCalendarMonthHeader';
|
||||
import { RecordCalendarMonthContextProvider } from '@/object-record/record-calendar/month/contexts/RecordCalendarMonthContext';
|
||||
import { useRecordCalendarMonthDaysRange } from '@/object-record/record-calendar/month/hooks/useRecordCalendarMonthDaysRange';
|
||||
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { useHandleDragOneCalendarCard } from '@/object-record/record-drag/calendar/hooks/useHandleDragOneCalendarCard';
|
||||
import { useEndRecordDrag } from '@/object-record/record-drag/shared/hooks/useEndRecordDrag';
|
||||
import { useStartRecordDrag } from '@/object-record/record-drag/shared/hooks/useStartRecordDrag';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
DragDropContext,
|
||||
type DragStart,
|
||||
type OnDragEndResponder,
|
||||
} from '@hello-pangea/dnd';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -13,10 +23,18 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export const RecordCalendarMonth = () => {
|
||||
const recordCalendarId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordCalendarComponentInstanceContext,
|
||||
);
|
||||
|
||||
const recordCalendarSelectedDate = useRecoilComponentValue(
|
||||
recordCalendarSelectedDateComponentState,
|
||||
);
|
||||
|
||||
const { processDragOperation } = useHandleDragOneCalendarCard();
|
||||
const { startDrag } = useStartRecordDrag('calendar', recordCalendarId);
|
||||
const { endDrag } = useEndRecordDrag('calendar', recordCalendarId);
|
||||
|
||||
const {
|
||||
firstDayOfMonth,
|
||||
lastDayOfMonth,
|
||||
@@ -27,6 +45,16 @@ export const RecordCalendarMonth = () => {
|
||||
weekStartsOnDayIndex,
|
||||
} = useRecordCalendarMonthDaysRange(recordCalendarSelectedDate);
|
||||
|
||||
const handleDragStart = (start: DragStart) => {
|
||||
startDrag(start, []);
|
||||
};
|
||||
|
||||
const handleDragEnd: OnDragEndResponder = (result) => {
|
||||
endDrag();
|
||||
if (!result.destination) return;
|
||||
processDragOperation(result);
|
||||
};
|
||||
|
||||
return (
|
||||
<RecordCalendarMonthContextProvider
|
||||
value={{
|
||||
@@ -39,10 +67,12 @@ export const RecordCalendarMonth = () => {
|
||||
weekStartsOnDayIndex,
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<RecordCalendarMonthHeader />
|
||||
<RecordCalendarMonthBody />
|
||||
</StyledContainer>
|
||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||
<StyledContainer>
|
||||
<RecordCalendarMonthHeader />
|
||||
<RecordCalendarMonthBody />
|
||||
</StyledContainer>
|
||||
</DragDropContext>
|
||||
</RecordCalendarMonthContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { RecordCalendarMonthBodyWeek } from '@/object-record/record-calendar/month/components/RecordCalendarMonthBodyWeek';
|
||||
import { useRecordCalendarMonthContextOrThrow } from '@/object-record/record-calendar/month/contexts/RecordCalendarMonthContext';
|
||||
import styled from '@emotion/styled';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -17,7 +18,7 @@ export const RecordCalendarMonthBody = () => {
|
||||
<StyledContainer>
|
||||
{weekFirstDays.map((weekFirstDay) => (
|
||||
<RecordCalendarMonthBodyWeek
|
||||
key={`week-${weekFirstDay.toDateString()}`}
|
||||
key={`week-${format(weekFirstDay, 'yyyy-MM-dd')}`}
|
||||
startDayOfWeek={weekFirstDay}
|
||||
/>
|
||||
))}
|
||||
|
||||
+37
-9
@@ -1,11 +1,12 @@
|
||||
import { RecordCalendarCard } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard';
|
||||
import { RecordCalendarCardDraggableContainer } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardDraggableContainer';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { calendarDayRecordIdsComponentFamilySelector } from '@/object-record/record-calendar/states/selectors/calendarDayRecordsComponentFamilySelector';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { isSameDay, isSameMonth } from 'date-fns';
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { format, isSameDay, isSameMonth } from 'date-fns';
|
||||
|
||||
const StyledContainer = styled.div<{ isOtherMonth: boolean }>`
|
||||
display: flex;
|
||||
@@ -49,10 +50,21 @@ const StyledDayHeader = styled.div<{ isToday: boolean }>`
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledCardsContainer = styled.div`
|
||||
const StyledCardsContainer = styled.div<{ isDraggedOver?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(0.5)};
|
||||
flex: 1;
|
||||
min-height: 60px;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
transition: background-color 0.1s ease;
|
||||
|
||||
${({ isDraggedOver, theme }) =>
|
||||
isDraggedOver &&
|
||||
css`
|
||||
background: ${theme.background.transparent.lighter};
|
||||
border: 1px dashed ${theme.border.color.medium};
|
||||
`}
|
||||
`;
|
||||
|
||||
type RecordCalendarMonthBodyDayProps = {
|
||||
@@ -66,9 +78,11 @@ export const RecordCalendarMonthBodyDay = ({
|
||||
recordCalendarSelectedDateComponentState,
|
||||
);
|
||||
|
||||
const dayKey = format(day, 'yyyy-MM-dd');
|
||||
|
||||
const recordIds = useRecoilComponentFamilyValue(
|
||||
calendarDayRecordIdsComponentFamilySelector,
|
||||
day.toDateString(),
|
||||
dayKey,
|
||||
);
|
||||
|
||||
const isToday = isSameDay(day, new Date());
|
||||
@@ -78,11 +92,25 @@ export const RecordCalendarMonthBodyDay = ({
|
||||
return (
|
||||
<StyledContainer isOtherMonth={isOtherMonth}>
|
||||
<StyledDayHeader isToday={isToday}>{day.getDate()}</StyledDayHeader>
|
||||
<StyledCardsContainer>
|
||||
{recordIds.slice(0, 5).map((recordId) => (
|
||||
<RecordCalendarCard key={recordId} recordId={recordId} />
|
||||
))}
|
||||
</StyledCardsContainer>
|
||||
<Droppable droppableId={dayKey}>
|
||||
{(droppableProvided, droppableSnapshot) => (
|
||||
<StyledCardsContainer
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...droppableProvided.droppableProps}
|
||||
ref={droppableProvided.innerRef}
|
||||
isDraggedOver={droppableSnapshot.isDraggingOver}
|
||||
>
|
||||
{recordIds.slice(0, 5).map((recordId, index) => (
|
||||
<RecordCalendarCardDraggableContainer
|
||||
key={recordId}
|
||||
recordId={recordId}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
{droppableProvided.placeholder}
|
||||
</StyledCardsContainer>
|
||||
)}
|
||||
</Droppable>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-8
@@ -4,7 +4,6 @@ import { RecordCalendarCardBody } from '@/object-record/record-calendar/record-c
|
||||
import { RecordCalendarCardHeader } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardHeader';
|
||||
import { RecordCalendarCardComponentInstanceContext } from '@/object-record/record-calendar/record-calendar-card/states/contexts/RecordCalendarCardComponentInstanceContext';
|
||||
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
|
||||
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
|
||||
@@ -22,16 +21,10 @@ type RecordCalendarCardProps = {
|
||||
};
|
||||
|
||||
export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
|
||||
const { openRecordFromIndexView } = useOpenRecordFromIndexView();
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const isCompactModeActive = currentView?.isCompact ?? false;
|
||||
|
||||
const handleCardClick = () => {
|
||||
openRecordFromIndexView({ recordId });
|
||||
};
|
||||
|
||||
return (
|
||||
<RecordCalendarCardComponentInstanceContext.Provider
|
||||
value={{
|
||||
@@ -39,7 +32,7 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<StyledRecordCard onClick={handleCardClick}>
|
||||
<StyledRecordCard>
|
||||
<RecordCalendarCardHeader recordId={recordId} />
|
||||
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
|
||||
<RecordCalendarCardBody
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
|
||||
import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { RecordCalendarCard } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard';
|
||||
import { RecordCalendarCardComponentInstanceContext } from '@/object-record/record-calendar/record-calendar-card/states/contexts/RecordCalendarCardComponentInstanceContext';
|
||||
|
||||
const StyledDraggableContainer = styled.div`
|
||||
position: relative;
|
||||
scroll-margin-left: 8px;
|
||||
scroll-margin-right: 8px;
|
||||
scroll-margin-top: 8px;
|
||||
`;
|
||||
|
||||
export const RecordCalendarCardDraggableContainer = ({
|
||||
recordId,
|
||||
index,
|
||||
}: {
|
||||
recordId: string;
|
||||
index: number;
|
||||
}) => {
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
|
||||
const isRecordReadOnly = useIsRecordReadOnly({
|
||||
recordId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
});
|
||||
|
||||
return (
|
||||
<RecordCalendarCardComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordId }}
|
||||
>
|
||||
<Draggable
|
||||
key={recordId}
|
||||
draggableId={recordId}
|
||||
index={index}
|
||||
isDragDisabled={isRecordReadOnly}
|
||||
>
|
||||
{(draggableProvided) => (
|
||||
<StyledDraggableContainer
|
||||
id={`record-calendar-card-${recordId}`}
|
||||
ref={draggableProvided?.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.dragHandleProps}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
data-selectable-id={recordId}
|
||||
>
|
||||
<RecordCalendarCard recordId={recordId} />
|
||||
</StyledDraggableContainer>
|
||||
)}
|
||||
</Draggable>
|
||||
</RecordCalendarCardComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
+16
-1
@@ -2,6 +2,8 @@ import { RecordChip } from '@/object-record/components/RecordChip';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { RecordCardHeaderContainer } from '@/object-record/record-card/components/RecordCardHeaderContainer';
|
||||
import { useRecordDragState } from '@/object-record/record-drag/shared/hooks/useRecordDragState';
|
||||
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
|
||||
@@ -28,13 +30,24 @@ type RecordCalendarCardHeaderProps = {
|
||||
export const RecordCalendarCardHeader = ({
|
||||
recordId,
|
||||
}: RecordCalendarCardHeaderProps) => {
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
const { objectMetadataItem, viewBarInstanceId } =
|
||||
useRecordCalendarContextOrThrow();
|
||||
const record = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
const { openRecordFromIndexView } = useOpenRecordFromIndexView();
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const isCompactModeActive = currentView?.isCompact ?? false;
|
||||
|
||||
const dragState = useRecordDragState('calendar', viewBarInstanceId);
|
||||
|
||||
const handleChipClick = () => {
|
||||
if (dragState.isDragging) {
|
||||
return;
|
||||
}
|
||||
openRecordFromIndexView({ recordId });
|
||||
};
|
||||
|
||||
if (!isDefined(record)) {
|
||||
return null;
|
||||
}
|
||||
@@ -48,6 +61,8 @@ export const RecordCalendarCardHeader = ({
|
||||
record={record}
|
||||
variant={ChipVariant.Transparent}
|
||||
isIconHidden={true}
|
||||
onClick={handleChipClick}
|
||||
triggerEvent={'CLICK'}
|
||||
/>
|
||||
</StopPropagationContainer>
|
||||
</StyledRecordChipContainer>
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector';
|
||||
import { isSameDay } from 'date-fns';
|
||||
import { isSameDay, parse } from 'date-fns';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const calendarDayRecordIdsComponentFamilySelector =
|
||||
@@ -54,7 +54,7 @@ export const calendarDayRecordIdsComponentFamilySelector =
|
||||
return false;
|
||||
}
|
||||
|
||||
const dayDate = new Date(dayAsString);
|
||||
const dayDate = parse(dayAsString, 'yyyy-MM-dd', new Date());
|
||||
const recordDateObj = new Date(recordDate);
|
||||
|
||||
return isSameDay(recordDateObj, dayDate);
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
import { type DropResult } from '@hello-pangea/dnd';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { calendarDayRecordIdsComponentFamilySelector } from '@/object-record/record-calendar/states/selectors/calendarDayRecordsComponentFamilySelector';
|
||||
import { calculateDragPositions } from '@/object-record/record-drag/shared/utils/calculateDragPositions';
|
||||
import { extractRecordPositions } from '@/object-record/record-drag/shared/utils/extractRecordPositions';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/ui/types/guards/isFieldDateTime';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import {
|
||||
formatISO,
|
||||
getHours,
|
||||
getMilliseconds,
|
||||
getMinutes,
|
||||
getSeconds,
|
||||
parse,
|
||||
set,
|
||||
} from 'date-fns';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useHandleDragOneCalendarCard = () => {
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const calendarDayRecordIdsSelector = useRecoilComponentCallbackState(
|
||||
calendarDayRecordIdsComponentFamilySelector,
|
||||
);
|
||||
|
||||
const processDragOperation = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (result: DropResult) => {
|
||||
if (!result.destination || !currentView?.calendarFieldMetadataId)
|
||||
return;
|
||||
|
||||
const { draggableId: recordId } = result;
|
||||
const destinationDate = result.destination.droppableId;
|
||||
const destinationIndex = result.destination.index;
|
||||
|
||||
const record = snapshot
|
||||
.getLoadable(recordStoreFamilyState(recordId))
|
||||
.getValue();
|
||||
|
||||
if (!record) return;
|
||||
|
||||
const calendarFieldMetadata = objectMetadataItem.fields.find(
|
||||
(field) => field.id === currentView.calendarFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!calendarFieldMetadata) return;
|
||||
|
||||
const destinationRecordIds = snapshot
|
||||
.getLoadable(calendarDayRecordIdsSelector(destinationDate))
|
||||
.getValue() as string[];
|
||||
|
||||
const recordPositionData = extractRecordPositions(
|
||||
destinationRecordIds,
|
||||
snapshot,
|
||||
);
|
||||
|
||||
const positions = calculateDragPositions({
|
||||
recordIds: destinationRecordIds,
|
||||
recordsToMove: [recordId],
|
||||
destinationIndex,
|
||||
recordPositionData,
|
||||
});
|
||||
|
||||
const newPosition = positions[recordId];
|
||||
|
||||
const targetDate = parse(destinationDate, 'yyyy-MM-dd', new Date());
|
||||
const currentFieldValue = record[calendarFieldMetadata.name];
|
||||
let newDate: Date;
|
||||
|
||||
if (
|
||||
isDefined(currentFieldValue) &&
|
||||
isFieldDateTime(calendarFieldMetadata)
|
||||
) {
|
||||
const currentDateTime = new Date(currentFieldValue);
|
||||
newDate = set(targetDate, {
|
||||
hours: getHours(currentDateTime),
|
||||
minutes: getMinutes(currentDateTime),
|
||||
seconds: getSeconds(currentDateTime),
|
||||
milliseconds: getMilliseconds(currentDateTime),
|
||||
});
|
||||
} else {
|
||||
newDate = targetDate;
|
||||
}
|
||||
|
||||
await updateOneRecord({
|
||||
idToUpdate: recordId,
|
||||
updateOneRecordInput: {
|
||||
[calendarFieldMetadata.name]: formatISO(newDate),
|
||||
position: newPosition,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
objectMetadataItem,
|
||||
currentView,
|
||||
updateOneRecord,
|
||||
calendarDayRecordIdsSelector,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
processDragOperation,
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export type RecordDragContext = 'board' | 'table';
|
||||
export type RecordDragContext = 'board' | 'table' | 'calendar';
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordTableRecordGqlFields = ({
|
||||
export const useRecordFieldGqlFields = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
|
||||
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
|
||||
|
||||
export const useRecordIndexTableFetchMore = (objectNameSingular: string) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
@@ -10,7 +10,7 @@ export const useRecordIndexTableFetchMore = (objectNameSingular: string) => {
|
||||
|
||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||
|
||||
const recordGqlFields = useRecordTableRecordGqlFields({ objectMetadataItem });
|
||||
const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem });
|
||||
|
||||
const { fetchMoreRecordsLazy, queryIdentifier } = useLazyFindManyRecords({
|
||||
...params,
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
|
||||
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
|
||||
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useRecordIndexTableQuery = (objectNameSingular: string) => {
|
||||
|
||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||
|
||||
const recordGqlFields = useRecordTableRecordGqlFields({ objectMetadataItem });
|
||||
const recordGqlFields = useRecordFieldGqlFields({ objectMetadataItem });
|
||||
|
||||
const { records, hasNextPage, queryIdentifier, loading } = useFindManyRecords(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user