"use client"; import { format } from "date-fns"; import { CalendarClock } from "lucide-react"; import type { ReactNode } from "react"; import { Controller, type UseFormReturn, useWatch } from "react-hook-form"; import { Badge, Button, Checkbox, Field, FieldLabel, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/shadcn"; import { formatDayOfMonth, formatScheduleHour, getBrowserTimezone, getNextScheduledRun, } from "@/lib/schedules"; import { useCloudUpgradeStore } from "@/store"; import { CLOUD_UPGRADE_FEATURE } from "@/types/cloud-upgrade"; import { SCHEDULE_FREQUENCY, SCHEDULE_WEEKDAY_LABELS, type ScheduleFormValues, } from "@/types/schedules"; // The INTERVAL label is resolved at render time from the form's intervalHours. const FREQUENCY_OPTIONS = [ { value: SCHEDULE_FREQUENCY.DAILY, label: "Daily" }, { value: SCHEDULE_FREQUENCY.INTERVAL, label: null }, { value: SCHEDULE_FREQUENCY.WEEKLY, label: "Weekly" }, { value: SCHEDULE_FREQUENCY.MONTHLY, label: "Monthly" }, ] as const; const WEEKDAY_OPTIONS = SCHEDULE_WEEKDAY_LABELS.map((label, value) => ({ value, label, })); const HOUR_OPTIONS = Array.from({ length: 24 }, (_, hour) => ({ value: hour, label: formatScheduleHour(hour), })); const MONTH_DAY_OPTIONS = Array.from({ length: 28 }, (_, index) => index + 1); interface ScanScheduleFieldsProps { form: UseFormReturn; disabled?: boolean; showLaunchInitialScan?: boolean; showNextScheduledCopy?: boolean; /** Rendered at the right of the "Scan Schedule" header row. */ headerAction?: ReactNode; /** * When false, the frequency is locked to `Daily` and the advanced cadences * (interval/weekly/monthly) are disabled. Used for non-Cloud (OSS) accounts. */ canUseAdvancedSchedule?: boolean; /** Render the "Available in Prowler Cloud" upsell badge in the header. */ showCloudUpgradeBadge?: boolean; } function NumberSelect({ label, value, values, onChange, disabled, }: { label: string; value: number; values: ReadonlyArray<{ value: number; label: string }>; onChange: (value: number) => void; disabled?: boolean; }) { return ( {label} ); } function getScheduleSummary({ frequency, intervalHours, dayOfWeek, dayOfMonth, }: Pick< ScheduleFormValues, "frequency" | "intervalHours" | "dayOfWeek" | "dayOfMonth" >) { switch (frequency) { case SCHEDULE_FREQUENCY.INTERVAL: return `Every ${intervalHours} hours`; case SCHEDULE_FREQUENCY.WEEKLY: return `Weekly on ${SCHEDULE_WEEKDAY_LABELS[dayOfWeek] ?? SCHEDULE_WEEKDAY_LABELS[0]}`; case SCHEDULE_FREQUENCY.MONTHLY: return `Monthly on the ${formatDayOfMonth(dayOfMonth)}`; default: return "Daily"; } } export function ScanScheduleFields({ form, disabled = false, showLaunchInitialScan = false, showNextScheduledCopy = false, headerAction, canUseAdvancedSchedule = true, showCloudUpgradeBadge = false, }: ScanScheduleFieldsProps) { const openCloudUpgrade = useCloudUpgradeStore( (state) => state.openCloudUpgrade, ); // useWatch, not form.watch: form.watch re-renders are dropped by React Compiler memoization. const control = form.control; const [frequency, hour, dayOfWeek, dayOfMonth, intervalHours] = useWatch({ control, name: ["frequency", "hour", "dayOfWeek", "dayOfMonth", "intervalHours"], }); const timezone = getBrowserTimezone(); const scheduleSummary = getScheduleSummary({ frequency, intervalHours, dayOfWeek, dayOfMonth, }); const frequencyLabel = (option: (typeof FREQUENCY_OPTIONS)[number]) => option.label ?? `Every ${intervalHours} hours`; // In OSS (non-Cloud) the advanced cadence and time are locked: `/schedules/daily` // ignores them, so they are display-only with a Cloud upsell. const advancedDisabled = disabled || !canUseAdvancedSchedule; const cloudUpgradeBadge = showCloudUpgradeBadge ? ( ) : null; return (

Scan Schedule

{cloudUpgradeBadge}
{headerAction}
( )} /> ( Repeats )} />
{frequency === SCHEDULE_FREQUENCY.WEEKLY && ( ( )} /> )} {frequency === SCHEDULE_FREQUENCY.MONTHLY && ( ( ({ value: day, label: String(day), }))} onChange={field.onChange} disabled={disabled} /> )} /> )} {showNextScheduledCopy && (canUseAdvancedSchedule ? (

{scheduleSummary}. The next scheduled scan will start on:{" "} {format( getNextScheduledRun( { frequency, hour, dayOfWeek, dayOfMonth, intervalHours, launchInitialScan: false, }, new Date(), ), "MMM d, yyyy", )}{" "} @ {formatScheduleHour(hour)} {timezone}

) : (

A daily scan will run automatically once the account is connected.

))} {showLaunchInitialScan && ( ( )} /> )}
); }