{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-range",
  "type": "registry:block",
  "title": "Date range",
  "description": "Calendar-date validation, retained failed values and guarded asynchronous apply.",
  "registryDependencies": [
    "button",
    "input"
  ],
  "files": [
    {
      "path": "blocks/date-range.tsx",
      "type": "registry:component",
      "target": "components/shine/date-range.tsx",
      "content": "\"use client\";\nimport { useId, useRef, useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nexport type DateRangeValue = { start: string; end: string };\nexport function dateRangeError(value: DateRangeValue): string | undefined {\n for (const [label, date] of [[\"Start\", value.start], [\"End\", value.end]]) {\n  if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(date) || !Number.isFinite(Date.parse(date)) || new Date(date).toISOString().slice(0, 10) !== date) return label + \" must be a valid date.\";\n }\n if (value.start > value.end) return \"End must be on or after start.\";\n}\n/** Dates are calendar values, never implicitly converted to the browser timezone. */\nexport function DateRange({ value, onChange, onApply, disabled = false, label = \"Date range\" }: {\n value: DateRangeValue; onChange: (value: DateRangeValue) => void; onApply: (value: DateRangeValue) => void | Promise<void>; disabled?: boolean; label?: string;\n}) {\n const id = useId(), lock = useRef(false), [busy, setBusy] = useState(false), [error, setError] = useState(\"\");\n async function apply() {\n  if (lock.current || disabled) return;\n  const invalid = dateRangeError(value); setError(invalid || \"\"); if (invalid) return;\n  lock.current = true; setBusy(true);\n  try { await onApply({ ...value }); } catch (cause) { setError(cause instanceof Error ? cause.message : \"Dates could not be applied. Try again.\"); }\n  finally { lock.current = false; setBusy(false); }\n }\n return <fieldset className=\"text-foreground min-w-0 space-y-2\" data-product-pattern=\"date-range\" disabled={disabled || busy} aria-describedby={error ? id : undefined}>\n  <legend className=\"mb-2 text-sm font-medium\">{label}</legend><div className=\"flex flex-wrap items-end gap-3\">\n   <label className=\"min-w-0 flex-1 basis-40 space-y-1\"><span className=\"text-sm\">Start</span><Input className=\"h-11 w-full\" type=\"date\" value={value.start} onChange={e => { onChange({ ...value, start: e.target.value }); setError(\"\"); }} /></label>\n   <label className=\"min-w-0 flex-1 basis-40 space-y-1\"><span className=\"text-sm\">End</span><Input className=\"h-11 w-full\" type=\"date\" value={value.end} onChange={e => { onChange({ ...value, end: e.target.value }); setError(\"\"); }} /></label>\n   <Button type=\"button\" variant=\"outline\" className=\"min-h-11\" onClick={() => void apply()}>{busy ? \"Applying…\" : \"Apply dates\"}</Button>\n  </div><p id={id} role=\"alert\" className=\"text-sm text-destructive\">{error}</p>\n </fieldset>;\n}\n"
    }
  ]
}
