'use client'; /** * MonthDayGrid * * Full-width calendar-style grid for selecting days of the month. * Clean, intuitive design like a mini calendar. */ import { cn } from '@djangocfg/ui-core/lib'; import { useCronMonthDays, useCronSize } from '../context/hooks'; import type { MonthDay } from '../types'; // Generate days 1-31 const DAYS: MonthDay[] = Array.from({ length: 31 }, (_, i) => (i + 1) as MonthDay); // Fill to complete last row (31 days = 4 full rows + 3 days, need 4 more to fill) const GRID_SIZE = 35; // 5 rows x 7 columns export interface MonthDayGridProps { disabled?: boolean; showPresets?: boolean; className?: string; } export function MonthDayGrid({ disabled, showPresets = true, className, }: MonthDayGridProps) { const { monthDays, toggleMonthDay, setMonthDays } = useCronMonthDays(); const isSm = useCronSize() === 'sm'; // Ensure monthDays is always an array const safeMonthDays = Array.isArray(monthDays) ? monthDays : []; // Prepare presets data const is1st = safeMonthDays.length === 1 && safeMonthDays[0] === 1; const is15th = safeMonthDays.length === 1 && safeMonthDays[0] === 15; const is1stAnd15th = safeMonthDays.length === 2 && safeMonthDays.includes(1) && safeMonthDays.includes(15); const presets = [ { label: '1st', isActive: is1st, days: [1] as MonthDay[] }, { label: '15th', isActive: is15th, days: [15] as MonthDay[] }, { label: '1st & 15th', isActive: is1stAnd15th, days: [1, 15] as MonthDay[] }, ]; // Prepare grid cells data const gridCells = Array.from({ length: GRID_SIZE }, (_, i) => { const day = i + 1; const isValidDay = day <= 31; const isSelected = isValidDay && safeMonthDays.includes(day as MonthDay); const isPartialMonth = day > 28; if (!isValidDay) { return { type: 'empty' as const, key: i }; } return { type: 'day' as const, key: day, day: day as MonthDay, isSelected, className: cn( 'flex items-center justify-center', 'font-medium', isSm ? 'h-7 rounded-[5px] text-[10px] leading-none' : 'aspect-square rounded-lg text-sm', 'transition-all duration-150', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50', 'active:scale-[0.95]', isSelected ? 'bg-primary text-primary-foreground shadow-sm' : cn( 'bg-muted/30 hover:bg-muted/60', isPartialMonth ? 'text-muted-foreground/50' : 'text-muted-foreground' ), disabled && 'opacity-50 cursor-not-allowed pointer-events-none' ), }; }); const selectionCount = safeMonthDays.length; return (
{/* Quick Presets */} {showPresets && (
{presets.map((preset) => ( setMonthDays(preset.days)} disabled={disabled} compact={isSm} /> ))}
)} {/* Calendar Grid - full width */}
{gridCells.map((cell) => { if (cell.type === 'empty') { return ( {/* Selection hint */} {selectionCount > 1 && (

{selectionCount} days selected

)}
); } interface PresetButtonProps { label: string; isActive: boolean; onClick: () => void; disabled?: boolean; compact?: boolean; } function PresetButton({ label, isActive, onClick, disabled, compact }: PresetButtonProps) { return ( ); }