mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 02:00:46 +08:00
Add config sync cycle stop and surface pause/stop controls in the UI.
Cancel remaining tasks on stop and keep finalize from overwriting a cancelled cycle. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
559cc14d2c
commit
1c8d512701
6 changed files with 124 additions and 5 deletions
|
|
@ -139,9 +139,12 @@ const en = {
|
|||
retryFailed: "Retry failed",
|
||||
pause: "Pause",
|
||||
resume: "Resume",
|
||||
stop: "Stop",
|
||||
confirmStop: "Stop this cycle and cancel remaining NE tasks?",
|
||||
started: "Sync cycle started",
|
||||
paused: "Paused",
|
||||
resumed: "Resumed",
|
||||
stopped: "Sync cycle stopped",
|
||||
policyTitle: "Sync policy",
|
||||
policySaved: "Policy saved",
|
||||
enabled: "Enable scheduled sync",
|
||||
|
|
@ -178,6 +181,7 @@ const en = {
|
|||
started: "Started",
|
||||
ended: "Ended",
|
||||
message: "Message",
|
||||
actions: "Actions",
|
||||
},
|
||||
},
|
||||
networkDevices: {
|
||||
|
|
|
|||
|
|
@ -139,9 +139,12 @@ const zh = {
|
|||
retryFailed: "一键重试失败",
|
||||
pause: "暂停",
|
||||
resume: "继续",
|
||||
stop: "停止",
|
||||
confirmStop: "停止后将取消未执行的网元任务,确定停止本轮同步?",
|
||||
started: "已启动同步周期",
|
||||
paused: "已暂停",
|
||||
resumed: "已继续",
|
||||
stopped: "已停止同步周期",
|
||||
policyTitle: "同步策略",
|
||||
policySaved: "策略已保存",
|
||||
enabled: "启用周期调度",
|
||||
|
|
@ -178,6 +181,7 @@ const zh = {
|
|||
started: "开始",
|
||||
ended: "结束",
|
||||
message: "消息",
|
||||
actions: "操作",
|
||||
},
|
||||
},
|
||||
networkDevices: {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
fetchConfigSyncDashboard,
|
||||
pauseConfigSyncCycle,
|
||||
resumeConfigSyncCycle,
|
||||
stopConfigSyncCycle,
|
||||
updateConfigSyncPolicy,
|
||||
} from "../services/api";
|
||||
import { queryKeys } from "../constants/queryKeys";
|
||||
|
|
@ -48,7 +49,9 @@ export function ConfigSyncPage() {
|
|||
staleTime: 1000,
|
||||
refetchInterval: (q) => {
|
||||
const running = q.state.data?.running_cycle;
|
||||
return running && (running.status === "running" || running.status === "paused") ? POLL_MS : false;
|
||||
return running && (running.status === "running" || running.status === "paused" || running.status === "pending")
|
||||
? POLL_MS
|
||||
: false;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -151,6 +154,15 @@ export function ConfigSyncPage() {
|
|||
onError: (err) => showError(String(err)),
|
||||
});
|
||||
|
||||
const stopMut = useMutation({
|
||||
mutationFn: (id: string) => stopConfigSyncCycle(id),
|
||||
onSuccess: async () => {
|
||||
showOk(t("configSync.stopped"));
|
||||
await refresh();
|
||||
},
|
||||
onError: (err) => showError(String(err)),
|
||||
});
|
||||
|
||||
const dash = dashQuery.data;
|
||||
const running = dash?.running_cycle;
|
||||
const last = dash?.last_cycle;
|
||||
|
|
@ -193,7 +205,7 @@ export function ConfigSyncPage() {
|
|||
>
|
||||
{t("configSync.retryFailed")}
|
||||
</button>
|
||||
{running?.status === "running" ? (
|
||||
{running?.status === "running" || running?.status === "pending" ? (
|
||||
<button type="button" onClick={() => pauseMut.mutate(running.id)} disabled={pauseMut.isPending}>
|
||||
{t("configSync.pause")}
|
||||
</button>
|
||||
|
|
@ -203,6 +215,17 @@ export function ConfigSyncPage() {
|
|||
{t("configSync.resume")}
|
||||
</button>
|
||||
) : null}
|
||||
{running && (running.status === "running" || running.status === "paused" || running.status === "pending") ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (window.confirm(t("configSync.confirmStop"))) stopMut.mutate(running.id);
|
||||
}}
|
||||
disabled={stopMut.isPending}
|
||||
>
|
||||
{t("configSync.stop")}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -369,6 +392,7 @@ export function ConfigSyncPage() {
|
|||
<th>{t("configSync.col.progress")}</th>
|
||||
<th>{t("configSync.col.started")}</th>
|
||||
<th>{t("configSync.col.ended")}</th>
|
||||
<th>{t("configSync.col.actions")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -393,11 +417,36 @@ export function ConfigSyncPage() {
|
|||
</td>
|
||||
<td>{c.started_at ? formatSystemTime(c.started_at) : "-"}</td>
|
||||
<td>{c.ended_at ? formatSystemTime(c.ended_at) : "-"}</td>
|
||||
<td>
|
||||
<div className="btn-row">
|
||||
{c.status === "running" || c.status === "pending" ? (
|
||||
<button type="button" onClick={() => pauseMut.mutate(c.id)} disabled={pauseMut.isPending}>
|
||||
{t("configSync.pause")}
|
||||
</button>
|
||||
) : null}
|
||||
{c.status === "paused" ? (
|
||||
<button type="button" onClick={() => resumeMut.mutate(c.id)} disabled={resumeMut.isPending}>
|
||||
{t("configSync.resume")}
|
||||
</button>
|
||||
) : null}
|
||||
{c.status === "running" || c.status === "paused" || c.status === "pending" ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (window.confirm(t("configSync.confirmStop"))) stopMut.mutate(c.id);
|
||||
}}
|
||||
disabled={stopMut.isPending}
|
||||
>
|
||||
{t("configSync.stop")}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{!cycles.length ? (
|
||||
<tr>
|
||||
<td colSpan={7} className="muted">
|
||||
<td colSpan={8} className="muted">
|
||||
{t("configSync.cyclesEmpty")}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -708,6 +708,9 @@ export const pauseConfigSyncCycle = (cycleId: string) =>
|
|||
export const resumeConfigSyncCycle = (cycleId: string) =>
|
||||
apiPost<ConfigSyncCycle>(`/v1/config-sync/cycles/${encodeURIComponent(cycleId)}/resume`, {});
|
||||
|
||||
export const stopConfigSyncCycle = (cycleId: string) =>
|
||||
apiPost<ConfigSyncCycle>(`/v1/config-sync/cycles/${encodeURIComponent(cycleId)}/stop`, {});
|
||||
|
||||
export const fetchConfigSyncCycleTasks = (params: {
|
||||
cycleId: string;
|
||||
page?: number;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue