handle drag n drop
This commit is contained in:
93
apps/web/src/hooks/use-entry-moved-mutation.ts
Normal file
93
apps/web/src/hooks/use-entry-moved-mutation.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { getTimeline } from "@/functions/get-timeline";
|
||||
import { updateTimelineItem } from "@/functions/update-timeline-item";
|
||||
|
||||
type TimelineData = Awaited<ReturnType<typeof getTimeline>>;
|
||||
|
||||
type EntryMovedVars = {
|
||||
entryId: string;
|
||||
newStart: string;
|
||||
newEnd: string | null;
|
||||
newGroupId: string;
|
||||
newLane: number;
|
||||
};
|
||||
|
||||
export function useEntryMovedMutation(timelineId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
const queryKey = ["timeline", timelineId];
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (vars: EntryMovedVars) =>
|
||||
updateTimelineItem({
|
||||
data: {
|
||||
id: vars.entryId,
|
||||
start: vars.newStart,
|
||||
end: vars.newEnd,
|
||||
timelineGroupId: vars.newGroupId,
|
||||
lane: vars.newLane,
|
||||
},
|
||||
}),
|
||||
|
||||
onMutate: async (vars) => {
|
||||
// Cancel in-flight fetches so they don't overwrite our optimistic update
|
||||
await queryClient.cancelQueries({ queryKey });
|
||||
const previous = queryClient.getQueryData<TimelineData>(queryKey);
|
||||
|
||||
queryClient.setQueryData<TimelineData>(queryKey, (old) => {
|
||||
if (!old) return old;
|
||||
|
||||
let movedItem:
|
||||
| TimelineData["groups"][number]["items"][number]
|
||||
| undefined;
|
||||
|
||||
const groups = old.groups.map((group) => {
|
||||
const filtered = group.items.filter((item) => {
|
||||
if (item.id === vars.entryId) {
|
||||
movedItem = item;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return { ...group, items: filtered };
|
||||
});
|
||||
|
||||
if (!movedItem) return old;
|
||||
|
||||
return {
|
||||
...old,
|
||||
groups: groups.map((group) =>
|
||||
group.id === vars.newGroupId
|
||||
? {
|
||||
...group,
|
||||
items: [
|
||||
...group.items,
|
||||
{
|
||||
...movedItem!,
|
||||
start: new Date(vars.newStart),
|
||||
end: vars.newEnd ? new Date(vars.newEnd) : null,
|
||||
lane: vars.newLane,
|
||||
timelineGroupId: vars.newGroupId,
|
||||
},
|
||||
],
|
||||
}
|
||||
: group
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
return { previous };
|
||||
},
|
||||
|
||||
onError: (_err, _vars, context) => {
|
||||
// Roll back to the previous cache state on server error
|
||||
if (context?.previous) {
|
||||
queryClient.setQueryData(queryKey, context.previous);
|
||||
}
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
// Re-fetch from server to ensure consistency
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user