({
job,
onToggle,
onSavePrompt,
onDelete,
onRun,
}: CronJobCardProps)
| 287 | } |
| 288 | |
| 289 | export function CronJobCard({ |
| 290 | job, |
| 291 | onToggle, |
| 292 | onSavePrompt, |
| 293 | onDelete, |
| 294 | onRun, |
| 295 | }: CronJobCardProps) { |
| 296 | const [expanded, setExpanded] = useState(false); |
| 297 | const [isEditingPrompt, setIsEditingPrompt] = useState(false); |
| 298 | const [draftPrompt, setDraftPrompt] = useState(job.detailsText?.trim() || ""); |
| 299 | const [isSavingPrompt, setIsSavingPrompt] = useState(false); |
| 300 | |
| 301 | const health = useMemo(() => getHealthMeta(job), [job]); |
| 302 | const timing = useMemo(() => getTimeMeta(job), [job]); |
| 303 | const StatusIcon = getStatusIcon(health.tone); |
| 304 | const promptText = job.detailsText?.trim() || job.description || ""; |
| 305 | |
| 306 | useEffect(() => { |
| 307 | if (!isEditingPrompt) { |
| 308 | setDraftPrompt(promptText); |
| 309 | } |
| 310 | }, [promptText, isEditingPrompt]); |
| 311 | |
| 312 | const handlePromptSave = async () => { |
| 313 | const trimmedPrompt = draftPrompt.trim(); |
| 314 | if (!trimmedPrompt || trimmedPrompt === promptText) { |
| 315 | setIsEditingPrompt(false); |
| 316 | setDraftPrompt(promptText); |
| 317 | return; |
| 318 | } |
| 319 | |
| 320 | setIsSavingPrompt(true); |
| 321 | try { |
| 322 | await onSavePrompt(job.id, trimmedPrompt); |
| 323 | setIsEditingPrompt(false); |
| 324 | } finally { |
| 325 | setIsSavingPrompt(false); |
| 326 | } |
| 327 | }; |
| 328 | |
| 329 | return ( |
| 330 | <div |
| 331 | style={{ |
| 332 | backgroundColor: "var(--card)", |
| 333 | border: `1px solid ${job.hasIssue ? "color-mix(in srgb, var(--error) 22%, var(--border))" : "var(--border)"}`, |
| 334 | borderRadius: "1rem", |
| 335 | padding: "0.95rem", |
| 336 | boxShadow: job.hasIssue ? "0 0 0 1px color-mix(in srgb, var(--error) 14%, transparent)" : "none", |
| 337 | minHeight: "100%", |
| 338 | }} |
| 339 | > |
| 340 | <div style={{ display: "flex", justifyContent: "space-between", gap: "0.85rem", alignItems: "flex-start" }}> |
| 341 | <div style={{ flex: 1, minWidth: 0 }}> |
| 342 | <div style={{ display: "flex", alignItems: "center", gap: "0.45rem", flexWrap: "wrap" }}> |
| 343 | <span style={{ fontSize: "1.25rem", lineHeight: 1 }}>{getAgentEmoji(job.agentId)}</span> |
| 344 | <h3 |
| 345 | style={{ |
| 346 | fontSize: "1rem", |
nothing calls this directly
no test coverage detected