'use client';
/**
* CronPreview
*
* Read-only sibling of `CronScheduler`. Renders a human-readable summary
* for a cron expression plus an optional list of upcoming run times.
*
* Unlike `SchedulePreview` (which lives inside the editor and pulls its
* state from `CronSchedulerProvider`), `CronPreview` is fully standalone:
* pass a value, get a preview. Use it in dashboards, list rows, and
* read-only schedule views where the heavier editor is not needed.
*
* @example
*
*
* @example
*
*/
import * as React from 'react';
import { Calendar } from 'lucide-react';
import { cn } from '@djangocfg/ui-core/lib';
import { humanizeCron } from '../utils/cron-humanize';
import { getNextRuns, formatNextRun } from '../utils/cron-next-runs';
import { isValidCron } from '../utils/cron-parser';
export interface CronPreviewProps extends Omit, 'children'> {
/** Cron expression (standard 5-field format, e.g. `"0 9 * * 1-5"`). */
value: string;
/** Optional heading shown above the summary. */
title?: string;
/**
* Number of upcoming run times to display.
* `0` hides the list. Defaults to `5`.
*/
nextRuns?: number;
/** Show the raw cron expression alongside the summary. Defaults to `true`. */
showExpression?: boolean;
/** Base date for computing next runs. Defaults to "now". */
referenceDate?: Date;
/** Additional CSS classes for the outer container. */
className?: string;
}
export function CronPreview({
value,
title,
nextRuns = 5,
showExpression = true,
referenceDate,
className,
...rest
}: CronPreviewProps) {
const trimmed = (value ?? '').trim();
const valid = isValidCron(trimmed);
const summary = React.useMemo(() => humanizeCron(trimmed), [trimmed]);
const runs = React.useMemo(() => {
if (!valid || nextRuns <= 0) return [];
return getNextRuns(trimmed, nextRuns, referenceDate ?? new Date());
}, [trimmed, valid, nextRuns, referenceDate]);
if (!valid) {
return (
Invalid cron expression: {trimmed || '(empty)'}
);
}
return (
{/* Header */}
{title && (
{title}
)}
{summary}
{showExpression && (
{trimmed}
)}
{/* Next runs */}
{runs.length > 0 && (
Next {runs.length === 1 ? 'run' : `${runs.length} runs`}