({ job }: { job: CronTask })
| 46 | } |
| 47 | |
| 48 | function CronCard({ job }: { job: CronTask }) { |
| 49 | // `recurring` is undefined/true → recurring by convention; false → one-shot. |
| 50 | const oneShot = job.recurring === false; |
| 51 | return ( |
| 52 | <div className="border border-border bg-surface-0"> |
| 53 | <div className="flex flex-wrap items-center gap-2 border-b border-border px-3 py-2"> |
| 54 | <Pill tone={oneShot ? 'ephemeral' : 'lifecycle'} variant="outline"> |
| 55 | {oneShot ? 'one-shot' : 'recurring'} |
| 56 | </Pill> |
| 57 | <code className="font-mono text-[12px] text-fg-0">{job.cron}</code> |
| 58 | <span className="font-mono text-[11px] text-fg-3">{job.id}</span> |
| 59 | <CopyButton value={job.id} /> |
| 60 | <span |
| 61 | className="ml-auto font-mono text-[11px] text-fg-3 tabular" |
| 62 | title={formatAbsoluteTime(job.createdAt)} |
| 63 | > |
| 64 | created {formatRelativeTime(job.createdAt)} |
| 65 | </span> |
| 66 | </div> |
| 67 | <div className="px-3 py-2"> |
| 68 | <div className="text-[10px] uppercase tracking-[0.1em] text-fg-3">prompt</div> |
| 69 | <div className="mt-1 whitespace-pre-wrap break-words font-mono text-[12px] text-fg-1"> |
| 70 | {job.prompt} |
| 71 | </div> |
| 72 | </div> |
| 73 | <div className="grid grid-cols-1 gap-x-6 gap-y-1 border-t border-border px-3 py-2 md:grid-cols-2"> |
| 74 | <Field label="lastFiredAt"> |
| 75 | {job.lastFiredAt === undefined ? ( |
| 76 | <span className="text-fg-3">(never fired)</span> |
| 77 | ) : ( |
| 78 | <span title={formatAbsoluteTime(job.lastFiredAt)}> |
| 79 | {formatAbsoluteTime(job.lastFiredAt)} ({formatRelativeTime(job.lastFiredAt)}) |
| 80 | </span> |
| 81 | )} |
| 82 | </Field> |
| 83 | <Field label="createdAt">{formatAbsoluteTime(job.createdAt)}</Field> |
| 84 | </div> |
| 85 | </div> |
| 86 | ); |
| 87 | } |
| 88 | |
| 89 | function Field({ label, children }: { label: string; children: import('react').ReactNode }) { |
| 90 | return ( |
nothing calls this directly
no test coverage detected