{"version":3,"sources":["../../../../src/primitives/Scheduling/Admin/ServicesTable.tsx"],"names":[],"mappings":"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","file":"ServicesTable.mjs","sourcesContent":["import { useState, type FormEvent, type ReactNode } from 'react';\nimport type { UseSchedulingAdminResult } from '../../../hooks/useSchedulingAdmin';\nimport type { SchedulingService } from '../../../types/scheduling';\n\nexport interface SchedulingServicesTableProps {\n  admin: UseSchedulingAdminResult;\n  className?: string;\n  rowClassName?: string;\n  inputClassName?: string;\n  buttonClassName?: string;\n  /** Currency code for price formatting. Defaults to 'USD'. */\n  currency?: string;\n  /** Called after a successful create (e.g. to refresh another panel). */\n  onCreated?: (service: SchedulingService) => void;\n  /** Render override for an individual row. */\n  renderRow?: (\n    service: SchedulingService,\n    actions: {\n      update: (patch: Partial<SchedulingService>) => Promise<void>;\n      remove: () => Promise<void>;\n    },\n  ) => ReactNode;\n  /** Default add-row label text. */\n  addLabel?: ReactNode;\n}\n\nconst EMPTY_DRAFT: Partial<SchedulingService> = {\n  name: '',\n  duration_minutes: 30,\n  price_cents: 0,\n  active: true,\n};\n\n/**\n * Parse a dollar amount the user typed into integer cents. Empty / garbage\n * input falls through to 0 — never NaN. Tolerates a leading \"$\",\n * thousand-separator commas, trailing whitespace.\n *\n * Exported so consumers using a custom `renderRow` can reuse the same\n * conversion in their own price input (the default renderer uses it for\n * both the new-service form and the existing-row inline edit).\n *\n *   dollarsStringToCents(\"30\")       → 3000\n *   dollarsStringToCents(\"29.99\")    → 2999\n *   dollarsStringToCents(\"$1,200.50\") → 120050\n *   dollarsStringToCents(\"\")          → 0\n *   dollarsStringToCents(\"abc\")       → 0\n */\nexport function dollarsStringToCents(value: string): number {\n  if (!value) return 0;\n  const trimmed = value.trim();\n  if (!trimmed) return 0;\n  const normalized = trimmed.replace(/[$,\\s]/g, '');\n  const n = Number.parseFloat(normalized);\n  if (!Number.isFinite(n) || n < 0) return 0;\n  return Math.round(n * 100);\n}\n\n/**\n * Inverse of dollarsStringToCents — render an integer cent amount as a\n * dollars string suitable for `<input value=…>`. Returns \"\" for null /\n * zero / negative so the price input shows its placeholder when there's\n * nothing meaningful to display.\n *\n *   centsToDollarsString(3000)  → \"30\"\n *   centsToDollarsString(2999)  → \"29.99\"\n *   centsToDollarsString(0)     → \"\"\n *   centsToDollarsString(null)  → \"\"\n */\nexport function centsToDollarsString(cents: number | null | undefined): string {\n  if (cents == null || !Number.isFinite(cents) || cents <= 0) return '';\n  return cents % 100 === 0 ? String(cents / 100) : (cents / 100).toFixed(2);\n}\n\nexport function SchedulingServicesTable({\n  admin,\n  className,\n  rowClassName,\n  inputClassName,\n  buttonClassName,\n  currency = 'USD',\n  onCreated,\n  renderRow,\n  addLabel,\n}: SchedulingServicesTableProps) {\n  const [draft, setDraft] = useState<Partial<SchedulingService>>(EMPTY_DRAFT);\n  // Price is held as a separate string while the user types so we don't lose\n  // intermediate states (e.g. \"30.\" or \"\") to a too-eager Number() conversion.\n  // Converted to cents at submit time only.\n  const [draftPrice, setDraftPrice] = useState<string>('');\n\n  const submitNew = async (e: FormEvent<HTMLFormElement>) => {\n    e.preventDefault();\n    if (!draft.name) return;\n    const created = await admin.services.create({\n      ...draft,\n      price_cents: dollarsStringToCents(draftPrice),\n    });\n    if (created) {\n      setDraft(EMPTY_DRAFT);\n      setDraftPrice('');\n      onCreated?.(created);\n    }\n  };\n\n  return (\n    <div className={className}>\n      {admin.services.loading && admin.services.list.length === 0 ? (\n        <div>Loading services…</div>\n      ) : null}\n      {admin.services.error ? (\n        <div role=\"alert\">{admin.services.error}</div>\n      ) : null}\n\n      {admin.services.list.map((service) => {\n        const actions = {\n          update: async (patch: Partial<SchedulingService>) => {\n            await admin.services.update(service.id, patch);\n          },\n          remove: async () => {\n            await admin.services.remove(service.id);\n          },\n        };\n        if (renderRow) return <div key={service.id}>{renderRow(service, actions)}</div>;\n        return (\n          <div key={service.id} className={rowClassName}>\n            <input\n              type=\"text\"\n              defaultValue={service.name}\n              onBlur={(e) => {\n                const next = e.target.value.trim();\n                if (next && next !== service.name) actions.update({ name: next });\n              }}\n              className={inputClassName}\n            />\n            <input\n              type=\"number\"\n              min={5}\n              step={5}\n              defaultValue={service.duration_minutes}\n              onBlur={(e) => {\n                const next = Number(e.target.value);\n                if (next > 0 && next !== service.duration_minutes) {\n                  actions.update({ duration_minutes: next });\n                }\n              }}\n              className={inputClassName}\n            />\n            <input\n              type=\"text\"\n              inputMode=\"decimal\"\n              defaultValue={centsToDollarsString(service.price_cents)}\n              placeholder=\"0\"\n              aria-label={`Price for ${service.name} in dollars`}\n              onBlur={(e) => {\n                const next = dollarsStringToCents(e.target.value);\n                if (next !== (service.price_cents ?? 0)) {\n                  actions.update({ price_cents: next });\n                }\n              }}\n              className={inputClassName}\n            />\n            <span aria-live=\"polite\">\n              {formatPrice(service.price_cents ?? 0, currency)}\n            </span>\n            <button\n              type=\"button\"\n              className={buttonClassName}\n              onClick={() => actions.update({ active: !service.active })}\n            >\n              {service.active ? 'Active' : 'Hidden'}\n            </button>\n            <button\n              type=\"button\"\n              className={buttonClassName}\n              onClick={() => actions.remove()}\n            >\n              Delete\n            </button>\n          </div>\n        );\n      })}\n\n      <form onSubmit={submitNew} className={rowClassName}>\n        <input\n          type=\"text\"\n          required\n          placeholder=\"Service name\"\n          value={draft.name ?? ''}\n          onChange={(e) => setDraft((prev) => ({ ...prev, name: e.target.value }))}\n          className={inputClassName}\n        />\n        <input\n          type=\"number\"\n          min={5}\n          step={5}\n          placeholder=\"Duration (min)\"\n          value={draft.duration_minutes ?? 30}\n          onChange={(e) =>\n            setDraft((prev) => ({\n              ...prev,\n              duration_minutes: Number(e.target.value),\n            }))\n          }\n          className={inputClassName}\n        />\n        <input\n          type=\"text\"\n          inputMode=\"decimal\"\n          placeholder=\"Price (e.g. 30 or 29.99)\"\n          aria-label=\"Price in dollars\"\n          value={draftPrice}\n          onChange={(e) => setDraftPrice(e.target.value)}\n          className={inputClassName}\n        />\n        <button type=\"submit\" className={buttonClassName}>\n          {addLabel ?? 'Add service'}\n        </button>\n      </form>\n    </div>\n  );\n}\n\nfunction formatPrice(cents: number, currency: string): string {\n  try {\n    return (cents / 100).toLocaleString(undefined, {\n      style: 'currency',\n      currency,\n    });\n  } catch {\n    return `${(cents / 100).toFixed(2)}`;\n  }\n}\n"]}