trying to run a gitea workflow
Some checks failed
Some checks failed
This commit is contained in:
6
web/knip.json
Normal file
6
web/knip.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@latest/schema.json",
|
||||
"entry": ["src/main.tsx", "vite.config.ts"],
|
||||
"project": ["src/**/*.{ts,tsx}", "scripts/**/*.js"],
|
||||
"ignore": ["src/api/**", "dist/**", "node_modules/**"]
|
||||
}
|
||||
@@ -6,7 +6,9 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"typecheck": "tsc -b --pretty false",
|
||||
"lint": "eslint .",
|
||||
"knip": "npx --yes knip --config knip.json --production",
|
||||
"preview": "vite preview",
|
||||
"generate:api": "curl -s http://localhost:8080/api-spec/openapi.json > openapi.json && npx openapi-typescript-codegen --input ./openapi.json --output ./src/api --client axios --useOptions"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { X, ExternalLink, Loader2 } from "lucide-react";
|
||||
import { useExecution } from "@/hooks/useExecutions";
|
||||
import { X, ExternalLink, Loader2, XCircle } from "lucide-react";
|
||||
import { useExecution, useCancelExecution } from "@/hooks/useExecutions";
|
||||
import { useExecutionStream } from "@/hooks/useExecutionStream";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import type { ExecutionStatus } from "@/api";
|
||||
@@ -51,6 +51,7 @@ const ExecutionPreviewPanel = memo(function ExecutionPreviewPanel({
|
||||
}: ExecutionPreviewPanelProps) {
|
||||
const { data, isLoading, error } = useExecution(executionId);
|
||||
const execution = data?.data;
|
||||
const cancelExecution = useCancelExecution();
|
||||
|
||||
// Subscribe to real-time updates for this execution
|
||||
useExecutionStream({ executionId, enabled: true });
|
||||
@@ -70,6 +71,8 @@ const ExecutionPreviewPanel = memo(function ExecutionPreviewPanel({
|
||||
execution?.status === "scheduled" ||
|
||||
execution?.status === "requested";
|
||||
|
||||
const isCancellable = isRunning || execution?.status === "canceling";
|
||||
|
||||
const startedAt = execution?.started_at
|
||||
? new Date(execution.started_at)
|
||||
: null;
|
||||
@@ -100,6 +103,28 @@ const ExecutionPreviewPanel = memo(function ExecutionPreviewPanel({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
{isCancellable && (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (
|
||||
window.confirm(
|
||||
`Are you sure you want to cancel execution #${executionId}?`,
|
||||
)
|
||||
) {
|
||||
cancelExecution.mutate(executionId);
|
||||
}
|
||||
}}
|
||||
disabled={cancelExecution.isPending}
|
||||
className="p-1.5 text-gray-400 hover:text-red-600 rounded hover:bg-red-50 transition-colors"
|
||||
title="Cancel execution"
|
||||
>
|
||||
{cancelExecution.isPending ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<XCircle className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/executions/${executionId}`}
|
||||
className="p-1.5 text-gray-400 hover:text-blue-600 rounded hover:bg-gray-100 transition-colors"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExecutionsService } from "@/api";
|
||||
import type { ExecutionStatus } from "@/api";
|
||||
import { OpenAPI } from "@/api/core/OpenAPI";
|
||||
import { request as __request } from "@/api/core/request";
|
||||
import type { ExecutionResponse } from "@/api";
|
||||
|
||||
interface ExecutionsQueryParams {
|
||||
page?: number;
|
||||
@@ -112,6 +113,33 @@ export function useRequestExecution() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a running or pending execution.
|
||||
*
|
||||
* Calls POST /api/v1/executions/{id}/cancel. For workflow executions this
|
||||
* cascades to all incomplete child task executions on the server side.
|
||||
*/
|
||||
export function useCancelExecution() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (executionId: number) => {
|
||||
const response = await __request(OpenAPI, {
|
||||
method: "POST",
|
||||
url: "/api/v1/executions/{id}/cancel",
|
||||
path: { id: executionId },
|
||||
mediaType: "application/json",
|
||||
});
|
||||
return response as { data: ExecutionResponse };
|
||||
},
|
||||
onSuccess: (_data, executionId) => {
|
||||
// Invalidate the specific execution and the list
|
||||
queryClient.invalidateQueries({ queryKey: ["executions", executionId] });
|
||||
queryClient.invalidateQueries({ queryKey: ["executions"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useChildExecutions(parentId: number | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ["executions", { parent: parentId }],
|
||||
|
||||
@@ -12,14 +12,14 @@ function formatDuration(ms: number): string {
|
||||
const remainMins = mins % 60;
|
||||
return `${hrs}h ${remainMins}m`;
|
||||
}
|
||||
import { useExecution } from "@/hooks/useExecutions";
|
||||
import { useExecution, useCancelExecution } from "@/hooks/useExecutions";
|
||||
import { useAction } from "@/hooks/useActions";
|
||||
import { useExecutionStream } from "@/hooks/useExecutionStream";
|
||||
import { useExecutionHistory } from "@/hooks/useHistory";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { ExecutionStatus } from "@/api";
|
||||
import { useState, useMemo } from "react";
|
||||
import { RotateCcw, Loader2 } from "lucide-react";
|
||||
import { RotateCcw, Loader2, XCircle } from "lucide-react";
|
||||
import ExecuteActionModal from "@/components/common/ExecuteActionModal";
|
||||
import EntityHistoryPanel from "@/components/common/EntityHistoryPanel";
|
||||
import ExecutionArtifactsPanel from "@/components/executions/ExecutionArtifactsPanel";
|
||||
@@ -123,6 +123,7 @@ export default function ExecutionDetailPage() {
|
||||
const isWorkflow = !!actionData?.data?.workflow_def;
|
||||
|
||||
const [showRerunModal, setShowRerunModal] = useState(false);
|
||||
const cancelExecution = useCancelExecution();
|
||||
|
||||
// Fetch status history for the timeline
|
||||
const { data: historyData, isLoading: historyLoading } = useExecutionHistory(
|
||||
@@ -200,6 +201,9 @@ export default function ExecutionDetailPage() {
|
||||
execution.status === ExecutionStatus.SCHEDULED ||
|
||||
execution.status === ExecutionStatus.REQUESTED;
|
||||
|
||||
const isCancellable =
|
||||
isRunning || execution.status === ExecutionStatus.CANCELING;
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
@@ -236,19 +240,44 @@ export default function ExecutionDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowRerunModal(true)}
|
||||
disabled={!actionData?.data}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
title={
|
||||
!actionData?.data
|
||||
? "Loading action details..."
|
||||
: "Re-run this action with the same parameters"
|
||||
}
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
Re-Run
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
{isCancellable && (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (
|
||||
window.confirm(
|
||||
`Are you sure you want to cancel execution #${execution.id}?`,
|
||||
)
|
||||
) {
|
||||
cancelExecution.mutate(execution.id);
|
||||
}
|
||||
}}
|
||||
disabled={cancelExecution.isPending}
|
||||
className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
title="Cancel this execution"
|
||||
>
|
||||
{cancelExecution.isPending ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<XCircle className="h-4 w-4" />
|
||||
)}
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setShowRerunModal(true)}
|
||||
disabled={!actionData?.data}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
|
||||
title={
|
||||
!actionData?.data
|
||||
? "Loading action details..."
|
||||
: "Re-run this action with the same parameters"
|
||||
}
|
||||
>
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
Re-Run
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-600 mt-2">
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user