Message routing follows the project's real task key, account switches clear local workbench state, and the duty matrix writes the reassignment API instead of a local drill. Co-authored-by: Cursor <cursoragent@cursor.com>
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import type { ProjectSettings, WorkflowPlan } from '../types/projectSettings';
|
|
|
|
export type ProjectSettingsMode = 'loading' | 'planning' | 'live_edit' | 'frozen' | 'error';
|
|
|
|
export function resolveProjectSettingsMode(input: {
|
|
loading?: boolean;
|
|
settings?: Pick<ProjectSettings, 'can_edit_plan' | 'lifecycle_status' | 'live_instance' | 'plan_edit_mode'> | null;
|
|
loadError?: boolean;
|
|
}): ProjectSettingsMode {
|
|
if (input.loadError && !input.settings) return 'error';
|
|
if (input.loading && !input.settings) return 'loading';
|
|
const settings = input.settings;
|
|
if (!settings) return 'loading';
|
|
if (settings.plan_edit_mode === 'frozen') return 'frozen';
|
|
if (settings.plan_edit_mode === 'live') return 'live_edit';
|
|
if (settings.plan_edit_mode === 'planning') return 'planning';
|
|
const live = settings.live_instance;
|
|
const closed = Boolean(live && (live.status === 'completed' || live.status === 'terminated'));
|
|
if (closed) return 'frozen';
|
|
if (live) return 'live_edit';
|
|
if (!settings.can_edit_plan) return 'frozen';
|
|
if (settings.lifecycle_status === 'active') return 'frozen';
|
|
return 'planning';
|
|
}
|
|
|
|
export function shouldShowPlanEditor(mode: ProjectSettingsMode): boolean {
|
|
return mode === 'planning' || mode === 'live_edit';
|
|
}
|
|
|
|
export function shouldShowSavePreview(mode: ProjectSettingsMode): boolean {
|
|
return mode === 'planning' || mode === 'live_edit';
|
|
}
|
|
|
|
export function projectSettingsChrome(mode: ProjectSettingsMode): {
|
|
showWorkflowSelector: boolean;
|
|
showDutySelects: boolean;
|
|
showSavePreview: boolean;
|
|
showStepPills: boolean;
|
|
showFrozenSnapshot: boolean;
|
|
showDutyReassign: boolean;
|
|
} {
|
|
const planning = mode === 'planning';
|
|
const live = mode === 'live_edit';
|
|
const frozen = mode === 'frozen';
|
|
return {
|
|
showWorkflowSelector: planning,
|
|
showDutySelects: planning || live,
|
|
showSavePreview: planning || live,
|
|
showStepPills: planning,
|
|
showFrozenSnapshot: frozen,
|
|
showDutyReassign: frozen,
|
|
};
|
|
}
|
|
|
|
export function afterFrozenConflictReload(
|
|
settings: Pick<ProjectSettings, 'can_edit_plan' | 'lifecycle_status' | 'live_instance'>,
|
|
draftPlan: WorkflowPlan,
|
|
): { plan: WorkflowPlan; mode: ProjectSettingsMode } {
|
|
return {
|
|
plan: draftPlan,
|
|
mode: resolveProjectSettingsMode({ settings }),
|
|
};
|
|
}
|
|
|
|
export function frozenDutySnapshot(plan?: WorkflowPlan | null): Array<{
|
|
taskKey: string;
|
|
title: string;
|
|
author?: number;
|
|
reviewer?: number;
|
|
approver?: number;
|
|
}> {
|
|
if (!plan) return [];
|
|
return plan.stages.flatMap((stage) =>
|
|
stage.doc_tasks
|
|
.filter((task) => task.enabled)
|
|
.map((task) => ({
|
|
taskKey: task.task_key,
|
|
title: task.task_title || task.task_key,
|
|
author: task.duties.author_user_id,
|
|
reviewer: task.duties.reviewer_user_id,
|
|
approver: task.duties.approver_user_id,
|
|
})),
|
|
);
|
|
}
|