mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-27 21:33:41 +02:00
* MUL-5202: migrate status issue surfaces to table query Co-authored-by: multica-agent <github@multica.ai> * MUL-5202: unify grouped issue surfaces Co-authored-by: multica-agent <github@multica.ai> * MUL-5202: cover move safety boundaries Co-authored-by: multica-agent <github@multica.ai> * MUL-5202: preserve server swimlane semantics Co-authored-by: multica-agent <github@multica.ai> * MUL-5202: keep grouped surface facets exact Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Eve <eve@multica-ai.local> Co-authored-by: multica-agent <github@multica.ai>
147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useMemo } from "react";
|
|
import { toast } from "sonner";
|
|
import type { UpdateIssueRequest } from "@multica/core/types";
|
|
import {
|
|
useBatchDeleteIssues,
|
|
useBatchUpdateIssues,
|
|
useUpdateIssue,
|
|
} from "@multica/core/issues/mutations";
|
|
import { useModalStore } from "@multica/core/modals";
|
|
import {
|
|
type IssueSurfaceActions,
|
|
type IssueSurfaceMutationOptions,
|
|
} from "./actions-context";
|
|
import type { IssueCreateDefaults } from "./types";
|
|
import { useT } from "../../i18n";
|
|
|
|
export type MoveIssueUpdates = Pick<
|
|
UpdateIssueRequest,
|
|
| "status"
|
|
| "assignee_type"
|
|
| "assignee_id"
|
|
| "position"
|
|
| "parent_issue_id"
|
|
| "project_id"
|
|
> & {
|
|
before_id: string | null;
|
|
after_id: string | null;
|
|
};
|
|
|
|
export interface IssueSurfaceActionController {
|
|
actions: IssueSurfaceActions;
|
|
openCreateIssue: (defaults?: IssueCreateDefaults) => void;
|
|
moveIssue: (
|
|
issueId: string,
|
|
updates: MoveIssueUpdates,
|
|
onSettled?: () => void,
|
|
) => void;
|
|
}
|
|
|
|
export function useIssueSurfaceActions({
|
|
createDefaults,
|
|
}: {
|
|
createDefaults: IssueCreateDefaults;
|
|
}): IssueSurfaceActionController {
|
|
const { t } = useT("projects");
|
|
const updateIssueMutation = useUpdateIssue();
|
|
const batchUpdateMutation = useBatchUpdateIssues();
|
|
const batchDeleteMutation = useBatchDeleteIssues();
|
|
|
|
const updateIssue = useCallback(
|
|
(
|
|
issueId: string,
|
|
updates: Partial<UpdateIssueRequest>,
|
|
options?: IssueSurfaceMutationOptions,
|
|
) => {
|
|
updateIssueMutation.mutate(
|
|
{ id: issueId, ...updates },
|
|
{
|
|
onSuccess: () => options?.onSuccess?.(),
|
|
onError: (err) => {
|
|
toast.error(
|
|
err instanceof Error && err.message
|
|
? err.message
|
|
: (options?.errorMessage ??
|
|
t(($) => $.detail.toast_move_issue_failed)),
|
|
);
|
|
options?.onError?.(err);
|
|
},
|
|
onSettled: () => options?.onSettled?.(),
|
|
},
|
|
);
|
|
},
|
|
[t, updateIssueMutation],
|
|
);
|
|
|
|
const moveIssue = useCallback(
|
|
(
|
|
issueId: string,
|
|
updates: MoveIssueUpdates,
|
|
onSettled?: () => void,
|
|
) => {
|
|
const { before_id, after_id, ...optimisticUpdates } = updates;
|
|
updateIssueMutation.mutate(
|
|
{
|
|
id: issueId,
|
|
...optimisticUpdates,
|
|
move_intent: { before_id, after_id },
|
|
},
|
|
{
|
|
onError: (err) => {
|
|
toast.error(
|
|
err instanceof Error && err.message
|
|
? err.message
|
|
: t(($) => $.detail.toast_move_issue_failed),
|
|
);
|
|
},
|
|
onSettled,
|
|
},
|
|
);
|
|
},
|
|
[t, updateIssueMutation],
|
|
);
|
|
|
|
const openCreateIssue = useCallback(
|
|
(defaults?: IssueCreateDefaults) => {
|
|
useModalStore
|
|
.getState()
|
|
.open("create-issue", { ...createDefaults, ...defaults });
|
|
},
|
|
[createDefaults],
|
|
);
|
|
|
|
const actions = useMemo<IssueSurfaceActions>(
|
|
() => ({
|
|
isPending:
|
|
updateIssueMutation.isPending ||
|
|
batchUpdateMutation.isPending ||
|
|
batchDeleteMutation.isPending,
|
|
createIssue: openCreateIssue,
|
|
updateIssue,
|
|
moveIssue: (issueId, updates, options) =>
|
|
updateIssue(issueId, updates, {
|
|
errorMessage: t(($) => $.detail.toast_move_issue_failed),
|
|
...options,
|
|
}),
|
|
batchUpdate: async (issueIds, updates) => {
|
|
await batchUpdateMutation.mutateAsync({ ids: issueIds, updates });
|
|
},
|
|
batchDelete: async (issueIds) => {
|
|
await batchDeleteMutation.mutateAsync(issueIds);
|
|
},
|
|
}),
|
|
[
|
|
batchDeleteMutation,
|
|
batchUpdateMutation,
|
|
openCreateIssue,
|
|
t,
|
|
updateIssue,
|
|
updateIssueMutation.isPending,
|
|
],
|
|
);
|
|
|
|
return { actions, openCreateIssue, moveIssue };
|
|
}
|