feat(web): schedule control, next-run, broken badge, run-log modal
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useRef, useState, type ChangeEvent, type FormEvent } from 'react'
|
||||
import { cancelAccount, createAccount, deleteAccount, getTask, importCSV, probeAccountFolders, probeFolders, runTask, setAccountFolderMapping, testAccounts, type TaskDetail as TaskDetailData } from '../api'
|
||||
import { cancelAccount, createAccount, deleteAccount, getTask, importCSV, probeAccountFolders, probeFolders, runTask, setAccountFolderMapping, setTaskSchedule, testAccounts, type TaskDetail as TaskDetailData } from '../api'
|
||||
import { connectTaskWS, type TaskEvent } from '../ws'
|
||||
import { StatusBadge } from '../components/StatusBadge'
|
||||
import { useConfirm } from '../components/ConfirmProvider'
|
||||
import { FolderMappingModal } from '../components/FolderMappingModal'
|
||||
import { RunLogModal } from '../components/RunLogModal'
|
||||
|
||||
const emptyAccount = { src_login: '', src_pass: '', dst_login: '', dst_pass: '' }
|
||||
|
||||
@@ -85,6 +86,7 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
const confirm = useConfirm()
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [live, setLive] = useState<Record<number, LiveProgress>>({})
|
||||
const [showRuns, setShowRuns] = useState(false)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
function reload() {
|
||||
@@ -164,7 +166,7 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
}
|
||||
|
||||
// Structural events refresh the persisted view; `progress` is covered by live state.
|
||||
if (['account_started', 'account_test', 'account_done', 'run_started', 'run_done', 'error', 'folder', 'cancelled', 'plan'].includes(ev.type)) {
|
||||
if (['account_started', 'account_test', 'account_done', 'run_started', 'run_done', 'error', 'folder', 'cancelled', 'plan', 'task_broken'].includes(ev.type)) {
|
||||
reload()
|
||||
}
|
||||
}),
|
||||
@@ -334,6 +336,16 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function onSchedule(intervalSeconds: number) {
|
||||
setError(null)
|
||||
try {
|
||||
await setTaskSchedule(id, intervalSeconds)
|
||||
reload()
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to set schedule')
|
||||
}
|
||||
}
|
||||
|
||||
if (notFound) {
|
||||
return (
|
||||
<div className="panel">
|
||||
@@ -374,6 +386,7 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
</h1>
|
||||
</div>
|
||||
<StatusBadge status={task.status} />
|
||||
{task.broken && <span className="badge badge-fail" style={{ marginLeft: 8 }}><span className="dot" />broken</span>}
|
||||
</div>
|
||||
|
||||
<div className="panel">
|
||||
@@ -406,6 +419,28 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
</button>
|
||||
{!allTested && accounts.length > 0 && <span className="hint">run unlocks once every account tests OK on both sides</span>}
|
||||
</div>
|
||||
<div className="sched-row">
|
||||
<label htmlFor="sched">Schedule</label>
|
||||
<select
|
||||
id="sched"
|
||||
value={task.schedule_interval_seconds ?? 0}
|
||||
onChange={(e) => onSchedule(Number(e.target.value))}
|
||||
>
|
||||
<option value={0}>Off</option>
|
||||
<option value={3600}>Every 1h</option>
|
||||
<option value={10800}>Every 3h</option>
|
||||
<option value={21600}>Every 6h</option>
|
||||
<option value={43200}>Every 12h</option>
|
||||
<option value={86400}>Every 24h</option>
|
||||
</select>
|
||||
{task.next_run_at && (
|
||||
<span className="sched-next">Next run: {new Date(task.next_run_at).toLocaleString()}</span>
|
||||
)}
|
||||
{task.broken && <span className="badge badge-fail"><span className="dot" />broken</span>}
|
||||
<button type="button" className="link-btn" onClick={() => setShowRuns(true)}>
|
||||
runs
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="panel-grid">
|
||||
@@ -635,6 +670,7 @@ export function TaskDetail({ id }: { id: number }) {
|
||||
onConfirm={saveEditMapping}
|
||||
/>
|
||||
)}
|
||||
<RunLogModal taskId={id} open={showRuns} onClose={() => setShowRuns(false)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user