jobs: after creating a job, show a View/Close confirmation instead of navigating away
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+36
-2
@@ -740,9 +740,10 @@ type ScriptRunnerProps = {
|
|||||||
entryType?: 'file' | 'directory';
|
entryType?: 'file' | 'directory';
|
||||||
filePath?: string;
|
filePath?: string;
|
||||||
selectedNames?: string[];
|
selectedNames?: string[];
|
||||||
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScriptRunner = ({ taskDirName, autoInputs, context, cwd, entryType, filePath, selectedNames }: ScriptRunnerProps) => {
|
const ScriptRunner = ({ taskDirName, autoInputs, context, cwd, entryType, filePath, selectedNames, onClose }: ScriptRunnerProps) => {
|
||||||
const runner = useTaskRunner();
|
const runner = useTaskRunner();
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -751,6 +752,8 @@ const ScriptRunner = ({ taskDirName, autoInputs, context, cwd, entryType, filePa
|
|||||||
const [inline, setInline] = useState(false);
|
const [inline, setInline] = useState(false);
|
||||||
// Is a job already running? (drives the Run vs Queue affordance).
|
// Is a job already running? (drives the Run vs Queue affordance).
|
||||||
const [jobRunning, setJobRunning] = useState(false);
|
const [jobRunning, setJobRunning] = useState(false);
|
||||||
|
// After a job is created, show a confirmation (View / Close) rather than navigating away.
|
||||||
|
const [created, setCreated] = useState<{ jobId: string; action: 'start' | 'queue' } | null>(null);
|
||||||
const bottomRef = useRef<HTMLDivElement | null>(null);
|
const bottomRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [inputDefs, setInputDefs] = useState<Record<string, TaskInputDef> | null>(null);
|
const [inputDefs, setInputDefs] = useState<Record<string, TaskInputDef> | null>(null);
|
||||||
const [formValues, setFormValues] = useState<Record<string, string>>({});
|
const [formValues, setFormValues] = useState<Record<string, string>>({});
|
||||||
@@ -938,12 +941,42 @@ const ScriptRunner = ({ taskDirName, autoInputs, context, cwd, entryType, filePa
|
|||||||
const submitJob = async (action: 'start' | 'queue') => {
|
const submitJob = async (action: 'start' | 'queue') => {
|
||||||
try {
|
try {
|
||||||
const { jobId } = await client.post<{ jobId: string }>('/jobs', { taskDirName, inputs: buildAllInputs(), cwd, action });
|
const { jobId } = await client.post<{ jobId: string }>('/jobs', { taskDirName, inputs: buildAllInputs(), cwd, action });
|
||||||
navigate(action === 'queue' ? '/jobs' : `/jobs/${jobId}`);
|
setCreated({ jobId, action });
|
||||||
} catch {
|
} catch {
|
||||||
/* stays on the modal so the user can retry */
|
/* stays on the modal so the user can retry */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (created) {
|
||||||
|
return (
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center gap-4 p-8 text-center">
|
||||||
|
<CircleCheck className="h-10 w-10 text-duck-teal" />
|
||||||
|
<div>
|
||||||
|
<div className="text-base font-medium text-duck-dark dark:text-foreground">
|
||||||
|
{created.action === 'queue' ? 'Job queued' : 'Job started'}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-duck-dark/50 dark:text-foreground/50 mt-1">
|
||||||
|
{created.action === 'queue' ? 'It will run when the current job finishes.' : "It’s running in the background."}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => { onClose(); navigate(created.action === 'queue' ? '/jobs' : `/jobs/${created.jobId}`); }}
|
||||||
|
className="flex items-center gap-2 px-5 py-2 rounded-lg bg-duck-teal text-white text-sm font-medium hover:bg-duck-teal/90 cursor-pointer"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4" /> {created.action === 'queue' ? 'View queue' : 'View job'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="px-5 py-2 rounded-lg text-sm font-medium text-duck-dark/70 dark:text-foreground/70 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 cursor-pointer"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (runner.phase === 'ready') {
|
if (runner.phase === 'ready') {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
@@ -1475,6 +1508,7 @@ export const TaskRunnerModal = ({ open, onOpenChange, task, entryName, entryFull
|
|||||||
entryType={effectiveEntryType}
|
entryType={effectiveEntryType}
|
||||||
filePath={multi ? cwd.path || undefined : entryName ? (cwd.path ? `${cwd.path}/${entryName}` : entryName) : undefined}
|
filePath={multi ? cwd.path || undefined : entryName ? (cwd.path ? `${cwd.path}/${entryName}` : entryName) : undefined}
|
||||||
selectedNames={multi ? selectedNames : undefined}
|
selectedNames={multi ? selectedNames : undefined}
|
||||||
|
onClose={() => onOpenChange(false)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PiMonoInner
|
<PiMonoInner
|
||||||
|
|||||||
Reference in New Issue
Block a user