fix(ui): complete grouped Jira dispatch review

This commit is contained in:
alejandrobailo committed 2026-07-21 13:06:25 +02:00
1 parent f1d26805d8
commit b6e1dd02c0
26 files changed
+1668 -894

No files matched your search

+141
View File
@@ -0,0 +1,141 @@
import type { JiraDispatchTaskResult } from "@/types/integrations";
import type { TaskState } from "@/types/tasks";
export interface JiraDispatchSuccessOutcome {
success: true;
message: string;
warning?: string;
failedFindingIds?: string[];
}
export interface JiraDispatchFailureOutcome {
success: false;
error: string;
failedFindingIds?: string[];
}
export type JiraDispatchOutcome =
| JiraDispatchSuccessOutcome
| JiraDispatchFailureOutcome;
const getArrayCount = (value: unknown[] | undefined) =>
Array.isArray(value) ? value.length : 0;
const getFailedCount = (result: JiraDispatchTaskResult | undefined) => {
if (!result) return 0;
return Math.max(
result.failed_count ?? 0,
getArrayCount(result.failed_groups),
getArrayCount(result.failed_batches),
getArrayCount(result.failed_finding_ids),
);
};
export const getJiraDispatchSuccessCount = (
result: JiraDispatchTaskResult | undefined,
) => {
if (!result) return 0;
const createdCount = Math.max(
result.created_count ?? 0,
getArrayCount(result.created_issues),
);
const updatedCount = Math.max(
result.updated_count ?? 0,
getArrayCount(result.updated_issues),
);
return Math.max(
result.successful_count ?? 0,
createdCount + updatedCount,
result.issue_key || result.issue_url ? 1 : 0,
);
};
const ensureSentence = (message: string) =>
/[.!?]$/.test(message.trim()) ? message.trim() : `${message.trim()}.`;
const buildFailureMessage = (
result: JiraDispatchTaskResult | undefined,
failedCount: number,
) => {
const successCount = getJiraDispatchSuccessCount(result);
const summary = `Jira dispatch completed with ${failedCount} failed and ${successCount} created/updated issue${successCount === 1 ? "" : "s"}.`;
return result?.error ? `${ensureSentence(result.error)} ${summary}` : summary;
};
const buildSuccessMessage = (result: JiraDispatchTaskResult | undefined) => {
const successCount = getJiraDispatchSuccessCount(result);
if (successCount > 1) {
return `${successCount} Jira issues were created or updated successfully.`;
}
return "Finding successfully sent to Jira!";
};
const getFailedFindingIds = (result: JiraDispatchTaskResult | undefined) =>
Array.from(new Set(result?.failed_finding_ids?.filter(Boolean) ?? []));
const withFailedFindingIds = (failedFindingIds: string[]) =>
failedFindingIds.length > 0 ? { failedFindingIds } : {};
export const evaluateJiraDispatchTask = (
state: TaskState,
result: JiraDispatchTaskResult | null | undefined,
): JiraDispatchOutcome => {
const jiraResult = result ?? undefined;
const failedFindingIds = getFailedFindingIds(jiraResult);
if (state === "completed") {
const failedCount = getFailedCount(jiraResult);
if (failedCount > 0) {
const successCount = getJiraDispatchSuccessCount(jiraResult);
if (successCount > 0) {
return {
success: true,
message: buildSuccessMessage(jiraResult),
warning: buildFailureMessage(jiraResult, failedCount),
...withFailedFindingIds(failedFindingIds),
};
}
return {
success: false,
error: buildFailureMessage(jiraResult, failedCount),
...withFailedFindingIds(failedFindingIds),
};
}
if (jiraResult?.success === false || jiraResult?.error) {
return {
success: false,
error: jiraResult.error || "Failed to create Jira issue.",
...withFailedFindingIds(failedFindingIds),
};
}
if (!jiraResult || getJiraDispatchSuccessCount(jiraResult) === 0) {
return {
success: false,
error:
"Jira dispatch completed but did not create or update any issues.",
};
}
return {
success: true,
message: buildSuccessMessage(jiraResult),
};
}
if (state === "failed") {
return {
success: false,
error: jiraResult?.error || "Task failed.",
...withFailedFindingIds(failedFindingIds),
};
}
return { success: false, error: `Unknown task state: ${state}` };
};
+84
View File
@@ -0,0 +1,84 @@
import {
JIRA_TARGET_SELECTION_KIND,
type JiraBatchSelection,
type JiraDispatchTarget,
type JiraDispatchTargetBatch,
type JiraSelection,
type NonEmptyStringArray,
} from "@/types/integrations";
export interface JiraDispatchTargetBatchInput {
targetIds: string[];
targetType: JiraDispatchTarget;
dispatchMode?: JiraDispatchTargetBatch["dispatchMode"];
}
export const toNonEmptyStringArray = (
values: string[],
): NonEmptyStringArray | null => {
const [first, ...rest] = values.filter(Boolean);
return first ? [first, ...rest] : null;
};
export const createJiraTargetSelection = (
targetIds: string[],
targetType: JiraDispatchTarget,
): JiraSelection | null => {
const nonEmptyTargetIds = toNonEmptyStringArray(targetIds);
if (!nonEmptyTargetIds) return null;
if (nonEmptyTargetIds.length === 1) {
return {
kind: JIRA_TARGET_SELECTION_KIND.SINGLE,
targetId: nonEmptyTargetIds[0],
targetType,
};
}
return {
kind: JIRA_TARGET_SELECTION_KIND.TARGET_LIST,
targetIds: nonEmptyTargetIds,
targetType,
};
};
export const createJiraBatchSelection = (
batches: JiraDispatchTargetBatchInput[],
): JiraBatchSelection | null => {
const normalizedBatches = batches.flatMap((batch) => {
const targetIds = toNonEmptyStringArray(batch.targetIds);
return targetIds ? [{ ...batch, targetIds }] : [];
});
const [firstBatch, ...remainingBatches] = normalizedBatches;
return firstBatch
? {
kind: JIRA_TARGET_SELECTION_KIND.BATCHES,
batches: [firstBatch, ...remainingBatches],
}
: null;
};
export const getJiraSelectionBatches = (
selection: JiraSelection,
): [JiraDispatchTargetBatch, ...JiraDispatchTargetBatch[]] => {
if (selection.kind === JIRA_TARGET_SELECTION_KIND.BATCHES) {
return selection.batches;
}
if (selection.kind === JIRA_TARGET_SELECTION_KIND.SINGLE) {
return [
{
targetIds: [selection.targetId],
targetType: selection.targetType,
},
];
}
return [
{
targetIds: selection.targetIds,
targetType: selection.targetType,
},
];
};
+46
View File
@@ -0,0 +1,46 @@
import type { WatchedTask } from "@/store/task-watcher/store";
import {
JIRA_DISPATCH_MODE,
type JiraDispatchMode,
} from "@/types/integrations";
export interface JiraDispatchTaskMeta {
integrationId: string;
projectKey: string;
issueType: string;
dispatchMode: JiraDispatchMode;
}
export const buildJiraDispatchTaskMeta = ({
integrationId,
projectKey,
issueType,
dispatchMode,
}: JiraDispatchTaskMeta): Record<string, string> => ({
integrationId,
projectKey,
issueType,
dispatchMode,
});
export const parseJiraDispatchTaskMeta = (
task: WatchedTask,
): JiraDispatchTaskMeta | null => {
const { integrationId, projectKey, issueType, dispatchMode } = task.meta;
if (
!integrationId ||
!projectKey ||
!issueType ||
(dispatchMode !== JIRA_DISPATCH_MODE.GROUPED &&
dispatchMode !== JIRA_DISPATCH_MODE.INDIVIDUAL)
) {
return null;
}
return {
integrationId,
projectKey,
issueType,
dispatchMode,
};
};