@cimplify/sdk 0.48.0 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react.js +136 -75
- package/dist/react.mjs +136 -75
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/registry/compact-service-card.json +1 -1
- package/registry/date-slot-picker.json +1 -1
- package/registry/product-sheet.json +1 -1
- package/registry/slot-picker.json +1 -1
package/dist/react.js
CHANGED
|
@@ -6282,37 +6282,74 @@ function SlotPicker({
|
|
|
6282
6282
|
radioGroup.RadioGroup,
|
|
6283
6283
|
{
|
|
6284
6284
|
"data-cimplify-slot-picker": true,
|
|
6285
|
-
className: cn(className, classNames?.root),
|
|
6285
|
+
className: cn("flex flex-col gap-4", className, classNames?.root),
|
|
6286
6286
|
value: selectedValue,
|
|
6287
6287
|
onValueChange: (value) => {
|
|
6288
6288
|
const slot = slotsByValue.get(value);
|
|
6289
|
-
if (slot
|
|
6289
|
+
if (slot && slot.is_available !== false) {
|
|
6290
6290
|
onSlotSelect?.(slot);
|
|
6291
6291
|
}
|
|
6292
6292
|
},
|
|
6293
|
-
children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6293
|
+
children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6294
|
+
"div",
|
|
6295
|
+
{
|
|
6296
|
+
"data-cimplify-slot-group": true,
|
|
6297
|
+
className: cn("flex flex-col gap-2", classNames?.group),
|
|
6298
|
+
children: [
|
|
6299
|
+
group.label && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6300
|
+
"div",
|
|
6301
|
+
{
|
|
6302
|
+
"data-cimplify-slot-group-label": true,
|
|
6303
|
+
className: cn(
|
|
6304
|
+
"text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground",
|
|
6305
|
+
classNames?.groupLabel
|
|
6306
|
+
),
|
|
6307
|
+
children: group.label
|
|
6308
|
+
}
|
|
6309
|
+
),
|
|
6310
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6311
|
+
"div",
|
|
6312
|
+
{
|
|
6313
|
+
className: cn(
|
|
6314
|
+
isMultiDay ? "flex flex-col gap-2" : "grid grid-cols-3 sm:grid-cols-4 gap-2"
|
|
6315
|
+
),
|
|
6316
|
+
children: group.slots.map((slot) => {
|
|
6317
|
+
const value = slotToValue(slot);
|
|
6318
|
+
const isSelected = selectedSlot?.start_time === slot.start_time && selectedSlot?.end_time === slot.end_time;
|
|
6319
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6320
|
+
radio.Radio.Root,
|
|
6321
|
+
{
|
|
6322
|
+
value,
|
|
6323
|
+
disabled: slot.is_available === false,
|
|
6324
|
+
"data-cimplify-slot": true,
|
|
6325
|
+
"data-selected": isSelected || void 0,
|
|
6326
|
+
"data-unavailable": slot.is_available === false || void 0,
|
|
6327
|
+
className: cn(
|
|
6328
|
+
"inline-flex items-center justify-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[unavailable]:cursor-not-allowed data-[unavailable]:opacity-40 data-[unavailable]:line-through",
|
|
6329
|
+
isMultiDay && "justify-between text-left",
|
|
6330
|
+
classNames?.slot
|
|
6331
|
+
),
|
|
6332
|
+
children: [
|
|
6333
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-slot-time": true, className: classNames?.slotTime, children: isMultiDay ? formatStaySummary(slot, durationUnit, durationValue) : formatTime(slot.start_time) }),
|
|
6334
|
+
showPrice && slot.price && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6335
|
+
"span",
|
|
6336
|
+
{
|
|
6337
|
+
"data-cimplify-slot-price": true,
|
|
6338
|
+
className: cn("text-xs opacity-70", classNames?.slotPrice),
|
|
6339
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: slot.price })
|
|
6340
|
+
}
|
|
6341
|
+
)
|
|
6342
|
+
]
|
|
6343
|
+
},
|
|
6344
|
+
value
|
|
6345
|
+
);
|
|
6346
|
+
})
|
|
6347
|
+
}
|
|
6348
|
+
)
|
|
6349
|
+
]
|
|
6350
|
+
},
|
|
6351
|
+
group.label || "all"
|
|
6352
|
+
))
|
|
6316
6353
|
}
|
|
6317
6354
|
);
|
|
6318
6355
|
}
|
|
@@ -6397,55 +6434,80 @@ function DateSlotPicker({
|
|
|
6397
6434
|
value: selectedDate,
|
|
6398
6435
|
onValueChange: handleDateChange,
|
|
6399
6436
|
"data-cimplify-date-slot-picker": true,
|
|
6400
|
-
className: cn(className, classNames?.root),
|
|
6437
|
+
className: cn("flex flex-col gap-4", className, classNames?.root),
|
|
6401
6438
|
children: [
|
|
6402
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6439
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6440
|
+
"div",
|
|
6441
|
+
{
|
|
6442
|
+
"data-cimplify-date-nav": true,
|
|
6443
|
+
className: cn("flex items-center justify-end gap-2", classNames?.nav),
|
|
6444
|
+
children: [
|
|
6445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6446
|
+
"button",
|
|
6447
|
+
{
|
|
6448
|
+
type: "button",
|
|
6449
|
+
onClick: handlePrev,
|
|
6450
|
+
disabled: offset === 0,
|
|
6451
|
+
"aria-label": "Previous dates",
|
|
6452
|
+
"data-cimplify-date-nav-prev": true,
|
|
6453
|
+
className: cn(
|
|
6454
|
+
"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40",
|
|
6455
|
+
classNames?.navButton
|
|
6456
|
+
),
|
|
6457
|
+
children: "\u2190"
|
|
6458
|
+
}
|
|
6459
|
+
),
|
|
6460
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6461
|
+
"button",
|
|
6462
|
+
{
|
|
6463
|
+
type: "button",
|
|
6464
|
+
onClick: handleNext,
|
|
6465
|
+
"aria-label": "Next dates",
|
|
6466
|
+
"data-cimplify-date-nav-next": true,
|
|
6467
|
+
className: cn(
|
|
6468
|
+
"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
6469
|
+
classNames?.navButton
|
|
6470
|
+
),
|
|
6471
|
+
children: "\u2192"
|
|
6472
|
+
}
|
|
6473
|
+
)
|
|
6474
|
+
]
|
|
6475
|
+
}
|
|
6476
|
+
),
|
|
6477
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6478
|
+
tabs.Tabs.List,
|
|
6479
|
+
{
|
|
6480
|
+
"data-cimplify-date-strip": true,
|
|
6481
|
+
className: cn("grid grid-cols-7 gap-1 sm:gap-2", classNames?.dateStrip),
|
|
6482
|
+
children: dateRange.dates.map((date) => {
|
|
6483
|
+
const dayInfo = availabilityMap.get(date);
|
|
6484
|
+
const hasAvailability = dayInfo?.has_availability !== false;
|
|
6485
|
+
const isSelected = selectedDate === date;
|
|
6486
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6487
|
+
tabs.Tabs.Tab,
|
|
6488
|
+
{
|
|
6489
|
+
value: date,
|
|
6490
|
+
"data-cimplify-date-button": true,
|
|
6491
|
+
"data-selected": isSelected || void 0,
|
|
6492
|
+
"data-available": hasAvailability || void 0,
|
|
6493
|
+
"data-fully-booked": !hasAvailability || void 0,
|
|
6494
|
+
className: cn(
|
|
6495
|
+
"flex flex-col items-center justify-center rounded-md border border-border bg-background px-1 py-2 text-center text-xs font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[fully-booked]:cursor-not-allowed data-[fully-booked]:opacity-40",
|
|
6496
|
+
classNames?.dateButton
|
|
6497
|
+
),
|
|
6498
|
+
children: formatDate(date)
|
|
6499
|
+
},
|
|
6500
|
+
date
|
|
6501
|
+
);
|
|
6502
|
+
})
|
|
6503
|
+
}
|
|
6504
|
+
),
|
|
6443
6505
|
availabilityLoading && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6444
6506
|
"div",
|
|
6445
6507
|
{
|
|
6446
6508
|
"data-cimplify-date-slot-loading": true,
|
|
6447
6509
|
"aria-busy": "true",
|
|
6448
|
-
className: classNames?.loading
|
|
6510
|
+
className: cn("h-32 rounded-md bg-muted/40 animate-pulse", classNames?.loading)
|
|
6449
6511
|
}
|
|
6450
6512
|
),
|
|
6451
6513
|
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-date-slots": true, className: classNames?.slots, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -7418,12 +7480,11 @@ function ProductSheet({
|
|
|
7418
7480
|
)
|
|
7419
7481
|
] }),
|
|
7420
7482
|
fullProduct.description && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7421
|
-
"
|
|
7483
|
+
"div",
|
|
7422
7484
|
{
|
|
7423
7485
|
"data-cimplify-product-sheet-description": true,
|
|
7424
|
-
className: classNames?.description,
|
|
7425
|
-
|
|
7426
|
-
children: fullProduct.description
|
|
7486
|
+
className: cn("text-sm leading-relaxed text-muted-foreground [&_p]:m-0 [&_p+p]:mt-2", classNames?.description),
|
|
7487
|
+
dangerouslySetInnerHTML: { __html: fullProduct.description }
|
|
7427
7488
|
}
|
|
7428
7489
|
),
|
|
7429
7490
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -7454,14 +7515,14 @@ function CardImage({
|
|
|
7454
7515
|
"16/9": "aspect-[16/9]"
|
|
7455
7516
|
}[aspectRatio];
|
|
7456
7517
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-card-image": true, className: cn("relative overflow-hidden bg-muted", aspectClass, className), children: [
|
|
7457
|
-
renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
7518
|
+
renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : src ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7458
7519
|
"img",
|
|
7459
7520
|
{
|
|
7460
7521
|
src,
|
|
7461
7522
|
alt,
|
|
7462
7523
|
className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
|
|
7463
7524
|
}
|
|
7464
|
-
),
|
|
7525
|
+
) : null,
|
|
7465
7526
|
children
|
|
7466
7527
|
] });
|
|
7467
7528
|
}
|
|
@@ -7913,7 +7974,7 @@ function CompactServiceCard({
|
|
|
7913
7974
|
const hasBillingPlans = product.billing_plans && product.billing_plans.length > 0;
|
|
7914
7975
|
const href = `/products/${product.slug}`;
|
|
7915
7976
|
const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 p-3", children: [
|
|
7916
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) }),
|
|
7977
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : image ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) : null }),
|
|
7917
7978
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
7918
7979
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
|
|
7919
7980
|
product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
|
package/dist/react.mjs
CHANGED
|
@@ -6277,37 +6277,74 @@ function SlotPicker({
|
|
|
6277
6277
|
RadioGroup,
|
|
6278
6278
|
{
|
|
6279
6279
|
"data-cimplify-slot-picker": true,
|
|
6280
|
-
className: cn(className, classNames?.root),
|
|
6280
|
+
className: cn("flex flex-col gap-4", className, classNames?.root),
|
|
6281
6281
|
value: selectedValue,
|
|
6282
6282
|
onValueChange: (value) => {
|
|
6283
6283
|
const slot = slotsByValue.get(value);
|
|
6284
|
-
if (slot
|
|
6284
|
+
if (slot && slot.is_available !== false) {
|
|
6285
6285
|
onSlotSelect?.(slot);
|
|
6286
6286
|
}
|
|
6287
6287
|
},
|
|
6288
|
-
children: groups.map((group) => /* @__PURE__ */ jsxs(
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6288
|
+
children: groups.map((group) => /* @__PURE__ */ jsxs(
|
|
6289
|
+
"div",
|
|
6290
|
+
{
|
|
6291
|
+
"data-cimplify-slot-group": true,
|
|
6292
|
+
className: cn("flex flex-col gap-2", classNames?.group),
|
|
6293
|
+
children: [
|
|
6294
|
+
group.label && /* @__PURE__ */ jsx(
|
|
6295
|
+
"div",
|
|
6296
|
+
{
|
|
6297
|
+
"data-cimplify-slot-group-label": true,
|
|
6298
|
+
className: cn(
|
|
6299
|
+
"text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground",
|
|
6300
|
+
classNames?.groupLabel
|
|
6301
|
+
),
|
|
6302
|
+
children: group.label
|
|
6303
|
+
}
|
|
6304
|
+
),
|
|
6305
|
+
/* @__PURE__ */ jsx(
|
|
6306
|
+
"div",
|
|
6307
|
+
{
|
|
6308
|
+
className: cn(
|
|
6309
|
+
isMultiDay ? "flex flex-col gap-2" : "grid grid-cols-3 sm:grid-cols-4 gap-2"
|
|
6310
|
+
),
|
|
6311
|
+
children: group.slots.map((slot) => {
|
|
6312
|
+
const value = slotToValue(slot);
|
|
6313
|
+
const isSelected = selectedSlot?.start_time === slot.start_time && selectedSlot?.end_time === slot.end_time;
|
|
6314
|
+
return /* @__PURE__ */ jsxs(
|
|
6315
|
+
Radio.Root,
|
|
6316
|
+
{
|
|
6317
|
+
value,
|
|
6318
|
+
disabled: slot.is_available === false,
|
|
6319
|
+
"data-cimplify-slot": true,
|
|
6320
|
+
"data-selected": isSelected || void 0,
|
|
6321
|
+
"data-unavailable": slot.is_available === false || void 0,
|
|
6322
|
+
className: cn(
|
|
6323
|
+
"inline-flex items-center justify-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[unavailable]:cursor-not-allowed data-[unavailable]:opacity-40 data-[unavailable]:line-through",
|
|
6324
|
+
isMultiDay && "justify-between text-left",
|
|
6325
|
+
classNames?.slot
|
|
6326
|
+
),
|
|
6327
|
+
children: [
|
|
6328
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-slot-time": true, className: classNames?.slotTime, children: isMultiDay ? formatStaySummary(slot, durationUnit, durationValue) : formatTime(slot.start_time) }),
|
|
6329
|
+
showPrice && slot.price && /* @__PURE__ */ jsx(
|
|
6330
|
+
"span",
|
|
6331
|
+
{
|
|
6332
|
+
"data-cimplify-slot-price": true,
|
|
6333
|
+
className: cn("text-xs opacity-70", classNames?.slotPrice),
|
|
6334
|
+
children: /* @__PURE__ */ jsx(Price, { amount: slot.price })
|
|
6335
|
+
}
|
|
6336
|
+
)
|
|
6337
|
+
]
|
|
6338
|
+
},
|
|
6339
|
+
value
|
|
6340
|
+
);
|
|
6341
|
+
})
|
|
6342
|
+
}
|
|
6343
|
+
)
|
|
6344
|
+
]
|
|
6345
|
+
},
|
|
6346
|
+
group.label || "all"
|
|
6347
|
+
))
|
|
6311
6348
|
}
|
|
6312
6349
|
);
|
|
6313
6350
|
}
|
|
@@ -6392,55 +6429,80 @@ function DateSlotPicker({
|
|
|
6392
6429
|
value: selectedDate,
|
|
6393
6430
|
onValueChange: handleDateChange,
|
|
6394
6431
|
"data-cimplify-date-slot-picker": true,
|
|
6395
|
-
className: cn(className, classNames?.root),
|
|
6432
|
+
className: cn("flex flex-col gap-4", className, classNames?.root),
|
|
6396
6433
|
children: [
|
|
6397
|
-
/* @__PURE__ */ jsxs(
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6434
|
+
/* @__PURE__ */ jsxs(
|
|
6435
|
+
"div",
|
|
6436
|
+
{
|
|
6437
|
+
"data-cimplify-date-nav": true,
|
|
6438
|
+
className: cn("flex items-center justify-end gap-2", classNames?.nav),
|
|
6439
|
+
children: [
|
|
6440
|
+
/* @__PURE__ */ jsx(
|
|
6441
|
+
"button",
|
|
6442
|
+
{
|
|
6443
|
+
type: "button",
|
|
6444
|
+
onClick: handlePrev,
|
|
6445
|
+
disabled: offset === 0,
|
|
6446
|
+
"aria-label": "Previous dates",
|
|
6447
|
+
"data-cimplify-date-nav-prev": true,
|
|
6448
|
+
className: cn(
|
|
6449
|
+
"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40",
|
|
6450
|
+
classNames?.navButton
|
|
6451
|
+
),
|
|
6452
|
+
children: "\u2190"
|
|
6453
|
+
}
|
|
6454
|
+
),
|
|
6455
|
+
/* @__PURE__ */ jsx(
|
|
6456
|
+
"button",
|
|
6457
|
+
{
|
|
6458
|
+
type: "button",
|
|
6459
|
+
onClick: handleNext,
|
|
6460
|
+
"aria-label": "Next dates",
|
|
6461
|
+
"data-cimplify-date-nav-next": true,
|
|
6462
|
+
className: cn(
|
|
6463
|
+
"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
6464
|
+
classNames?.navButton
|
|
6465
|
+
),
|
|
6466
|
+
children: "\u2192"
|
|
6467
|
+
}
|
|
6468
|
+
)
|
|
6469
|
+
]
|
|
6470
|
+
}
|
|
6471
|
+
),
|
|
6472
|
+
/* @__PURE__ */ jsx(
|
|
6473
|
+
Tabs.List,
|
|
6474
|
+
{
|
|
6475
|
+
"data-cimplify-date-strip": true,
|
|
6476
|
+
className: cn("grid grid-cols-7 gap-1 sm:gap-2", classNames?.dateStrip),
|
|
6477
|
+
children: dateRange.dates.map((date) => {
|
|
6478
|
+
const dayInfo = availabilityMap.get(date);
|
|
6479
|
+
const hasAvailability = dayInfo?.has_availability !== false;
|
|
6480
|
+
const isSelected = selectedDate === date;
|
|
6481
|
+
return /* @__PURE__ */ jsx(
|
|
6482
|
+
Tabs.Tab,
|
|
6483
|
+
{
|
|
6484
|
+
value: date,
|
|
6485
|
+
"data-cimplify-date-button": true,
|
|
6486
|
+
"data-selected": isSelected || void 0,
|
|
6487
|
+
"data-available": hasAvailability || void 0,
|
|
6488
|
+
"data-fully-booked": !hasAvailability || void 0,
|
|
6489
|
+
className: cn(
|
|
6490
|
+
"flex flex-col items-center justify-center rounded-md border border-border bg-background px-1 py-2 text-center text-xs font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[fully-booked]:cursor-not-allowed data-[fully-booked]:opacity-40",
|
|
6491
|
+
classNames?.dateButton
|
|
6492
|
+
),
|
|
6493
|
+
children: formatDate(date)
|
|
6494
|
+
},
|
|
6495
|
+
date
|
|
6496
|
+
);
|
|
6497
|
+
})
|
|
6498
|
+
}
|
|
6499
|
+
),
|
|
6438
6500
|
availabilityLoading && /* @__PURE__ */ jsx(
|
|
6439
6501
|
"div",
|
|
6440
6502
|
{
|
|
6441
6503
|
"data-cimplify-date-slot-loading": true,
|
|
6442
6504
|
"aria-busy": "true",
|
|
6443
|
-
className: classNames?.loading
|
|
6505
|
+
className: cn("h-32 rounded-md bg-muted/40 animate-pulse", classNames?.loading)
|
|
6444
6506
|
}
|
|
6445
6507
|
),
|
|
6446
6508
|
/* @__PURE__ */ jsx("div", { "data-cimplify-date-slots": true, className: classNames?.slots, children: /* @__PURE__ */ jsx(
|
|
@@ -7413,12 +7475,11 @@ function ProductSheet({
|
|
|
7413
7475
|
)
|
|
7414
7476
|
] }),
|
|
7415
7477
|
fullProduct.description && /* @__PURE__ */ jsx(
|
|
7416
|
-
"
|
|
7478
|
+
"div",
|
|
7417
7479
|
{
|
|
7418
7480
|
"data-cimplify-product-sheet-description": true,
|
|
7419
|
-
className: classNames?.description,
|
|
7420
|
-
|
|
7421
|
-
children: fullProduct.description
|
|
7481
|
+
className: cn("text-sm leading-relaxed text-muted-foreground [&_p]:m-0 [&_p+p]:mt-2", classNames?.description),
|
|
7482
|
+
dangerouslySetInnerHTML: { __html: fullProduct.description }
|
|
7422
7483
|
}
|
|
7423
7484
|
),
|
|
7424
7485
|
/* @__PURE__ */ jsx(
|
|
@@ -7449,14 +7510,14 @@ function CardImage({
|
|
|
7449
7510
|
"16/9": "aspect-[16/9]"
|
|
7450
7511
|
}[aspectRatio];
|
|
7451
7512
|
return /* @__PURE__ */ jsxs("div", { "data-cimplify-card-image": true, className: cn("relative overflow-hidden bg-muted", aspectClass, className), children: [
|
|
7452
|
-
renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : /* @__PURE__ */ jsx(
|
|
7513
|
+
renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : src ? /* @__PURE__ */ jsx(
|
|
7453
7514
|
"img",
|
|
7454
7515
|
{
|
|
7455
7516
|
src,
|
|
7456
7517
|
alt,
|
|
7457
7518
|
className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
|
|
7458
7519
|
}
|
|
7459
|
-
),
|
|
7520
|
+
) : null,
|
|
7460
7521
|
children
|
|
7461
7522
|
] });
|
|
7462
7523
|
}
|
|
@@ -7908,7 +7969,7 @@ function CompactServiceCard({
|
|
|
7908
7969
|
const hasBillingPlans = product.billing_plans && product.billing_plans.length > 0;
|
|
7909
7970
|
const href = `/products/${product.slug}`;
|
|
7910
7971
|
const content = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 p-3", children: [
|
|
7911
|
-
/* @__PURE__ */ jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) }),
|
|
7972
|
+
/* @__PURE__ */ jsx("div", { className: "w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0", children: renderImage ? renderImage({ src: image, alt: product.name, className: "w-full h-full object-cover" }) : image ? /* @__PURE__ */ jsx("img", { src: image, alt: product.name, className: "w-full h-full object-cover" }) : null }),
|
|
7912
7973
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
7913
7974
|
/* @__PURE__ */ jsx("h3", { className: "text-[14px] font-semibold text-foreground leading-tight truncate", children: product.name }),
|
|
7914
7975
|
product.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground mt-0.5 truncate", children: product.description }),
|
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-1\/2{top:50%}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[200\]{z-index:200}.z-\[9999\]{z-index:9999}.container{width:100%}.m-auto{margin:auto}.mx-auto{margin-inline:auto}.-mt-px{margin-top:-1px}.mr-auto{margin-right:auto}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[3\/4\]{aspect-ratio:3/4}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-\[5\/2\]{aspect-ratio:5/2}.aspect-\[16\/9\]{aspect-ratio:16/9}.aspect-square{aspect-ratio:1}.h-\[6px\]{height:6px}.h-\[7px\]{height:7px}.h-\[11px\]{height:11px}.h-\[18px\]{height:18px}.h-\[72px\]{height:72px}.h-\[min\(600px\,calc\(100vh-6rem\)\)\]{height:min(600px,100vh - 6rem)}.h-fit{height:fit-content}.h-full{height:100%}.max-h-\[85vh\]{max-height:85vh}.max-h-\[100px\]{max-height:100px}.min-h-\[20px\]{min-height:20px}.w-1\/2{width:50%}.w-2\/5{width:40%}.w-3\/4{width:75%}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-\[6px\]{width:6px}.w-\[7px\]{width:7px}.w-\[11px\]{width:11px}.w-\[18px\]{width:18px}.w-\[72px\]{width:72px}.w-\[400px\]{width:400px}.w-full{width:100%}.max-w-\[85\%\]{max-width:85%}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[300px\]{max-width:300px}.max-w-\[calc\(100vw-3rem\)\]{max-width:calc(100vw - 3rem)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.origin-bottom-left{transform-origin:0 100%}.origin-bottom-right{transform-origin:100% 100%}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize-none{resize:none}.list-none{list-style-type:none}.\[appearance\:textfield\]{appearance:textfield}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border,oklch(90% 0 0))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:var(--radius,.5rem)}.rounded-\[10px\]{border-radius:10px}.rounded-\[14px\]{border-radius:14px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-tl{border-top-left-radius:var(--radius,.5rem)}.rounded-tr{border-top-right-radius:var(--radius,.5rem)}.rounded-br{border-bottom-right-radius:var(--radius,.5rem)}.rounded-bl{border-bottom-left-radius:var(--radius,.5rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-foreground{border-color:var(--color-foreground,oklch(15% 0 0))}.border-input{border-color:var(--color-input,oklch(90% 0 0))}.border-muted-foreground\/30{border-color:#6363634d}@supports (color:color-mix(in lab, red, red)){.border-muted-foreground\/30{border-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.border-transparent{border-color:#0000}.border-t-foreground{border-top-color:var(--color-foreground,oklch(15% 0 0))}.bg-background{background-color:var(--color-background,oklch(99% 0 0))}.bg-background\/50{background-color:#fcfcfc80}@supports (color:color-mix(in lab, red, red)){.bg-background\/50{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 50%, transparent)}}.bg-background\/90{background-color:#fcfcfce6}@supports (color:color-mix(in lab, red, red)){.bg-background\/90{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 90%, transparent)}}.bg-background\/92{background-color:#fcfcfceb}@supports (color:color-mix(in lab, red, red)){.bg-background\/92{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 92%, transparent)}}.bg-border{background-color:var(--color-border,oklch(90% 0 0))}.bg-destructive{background-color:var(--color-destructive,oklch(50% .2 25))}.bg-destructive\/10{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.bg-foreground{background-color:var(--color-foreground,oklch(15% 0 0))}.bg-foreground\/40{background-color:#0b0b0b66}@supports (color:color-mix(in lab, red, red)){.bg-foreground\/40{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 40%, transparent)}}.bg-muted{background-color:var(--color-muted,oklch(95% 0 0))}.bg-muted-foreground\/40{background-color:#63636366}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/40{background-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 40%, transparent)}}.bg-muted\/40{background-color:#eee6}@supports (color:color-mix(in lab, red, red)){.bg-muted\/40{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 40%, transparent)}}.bg-muted\/50{background-color:#eeeeee80}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 50%, transparent)}}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/5{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.bg-primary\/5{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-cover{object-fit:cover}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-\[inherit\]{font-family:inherit}.text-\[9px\]{font-size:9px}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14\.5px\]{font-size:14.5px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[inherit\]{color:inherit}.text-background{color:var(--color-background,oklch(99% 0 0))}.text-border{color:var(--color-border,oklch(90% 0 0))}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-destructive-foreground{color:var(--color-destructive-foreground,oklch(99% 0 0))}.text-foreground{color:var(--color-foreground,oklch(15% 0 0))}.text-foreground\/80{color:#0b0b0bcc}@supports (color:color-mix(in lab, red, red)){.text-foreground\/80{color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 80%, transparent)}}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/30{color:#6363634d}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/30{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.text-muted-foreground\/40{color:#63636366}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/40{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 40%, transparent)}}.text-muted-foreground\/50{color:#63636380}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/50{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 50%, transparent)}}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-muted-foreground\/70{color:#636363b3}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/70{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 70%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.accent-foreground{accent-color:var(--color-foreground,oklch(15% 0 0))}.accent-primary{accent-color:var(--color-primary,oklch(50% .1 35))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-100{opacity:1}.shadow-\[0_1px_3px_rgba\(0\,0\,0\,0\.04\)\,0_6px_24px_rgba\(0\,0\,0\,0\.06\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#0000000a), 0 6px 24px var(--tw-shadow-color,#0000000f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_1px_4px_rgba\(0\,0\,0\,0\.10\)\,0_4px_12px_rgba\(0\,0\,0\,0\.08\)\]{--tw-shadow:0 1px 4px var(--tw-shadow-color,#0000001a), 0 4px 12px var(--tw-shadow-color,#00000014);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-background{--tw-ring-color:var(--color-background,oklch(99% 0 0))}.ring-primary\/40{--tw-ring-color:#934c3a66}@supports (color:color-mix(in lab, red, red)){.ring-primary\/40{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 40%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-\[1px\]{--tw-backdrop-blur:blur(1px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-\[cubic-bezier\(0\.19\,1\,0\.22\,1\)\]{--tw-ease:cubic-bezier(.19,1,.22,1);transition-timing-function:cubic-bezier(.19,1,.22,1)}.\[transition-timing-function\:cubic-bezier\(0\.16\,1\,0\.3\,1\)\]{transition-timing-function:cubic-bezier(.16,1,.3,1)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\[cimplify\:checkout\]{cimplify:checkout}.ring-inset{--tw-ring-inset:inset}@media (hover:hover){.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:scale-\[1\.04\]:is(:where(.group):hover *){scale:1.04}.group-hover\:text-foreground:is(:where(.group):hover *){color:var(--color-foreground,oklch(15% 0 0))}.group-hover\:text-primary:is(:where(.group):hover *){color:var(--color-primary,oklch(50% .1 35))}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-muted-foreground::placeholder{color:var(--color-muted-foreground,oklch(50% 0 0))}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.focus-within\:border-foreground:focus-within{border-color:var(--color-foreground,oklch(15% 0 0))}.focus-within\:bg-background:focus-within{background-color:var(--color-background,oklch(99% 0 0))}@media (hover:hover){.hover\:-translate-y-\[1px\]:hover{--tw-translate-y:calc(1px * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:-translate-y-px:hover{--tw-translate-y:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:\!scale-110:hover{--tw-scale-x:110%!important;--tw-scale-y:110%!important;--tw-scale-z:110%!important;scale:var(--tw-scale-x) var(--tw-scale-y)!important}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-\[1\.04\]:hover{scale:1.04}.hover\:scale-\[1\.08\]:hover{scale:1.08}.hover\:border-foreground:hover{border-color:var(--color-foreground,oklch(15% 0 0))}.hover\:border-primary\/20:hover{border-color:#934c3a33}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/20:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 20%, transparent)}}.hover\:border-primary\/40:hover{border-color:#934c3a66}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/40:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 40%, transparent)}}.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:\!bg-foreground:hover{background-color:var(--color-foreground,oklch(15% 0 0))!important}.hover\:bg-background:hover{background-color:var(--color-background,oklch(99% 0 0))}.hover\:bg-destructive\/10:hover{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/10:hover{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.hover\:bg-foreground\/90:hover{background-color:#0b0b0be6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-foreground\/90:hover{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 90%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-muted\/40:hover{background-color:#eee6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/40:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 40%, transparent)}}.hover\:bg-muted\/70:hover{background-color:#eeeeeeb3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/70:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 70%, transparent)}}.hover\:bg-muted\/80:hover{background-color:#eeec}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/80:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 80%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:\!text-background:hover{color:var(--color-background,oklch(99% 0 0))!important}.hover\:text-destructive:hover{color:var(--color-destructive,oklch(50% .2 25))}.hover\:text-foreground:hover{color:var(--color-foreground,oklch(15% 0 0))}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}.hover\:text-primary\/80:hover{color:#934c3acc}@supports (color:color-mix(in lab, red, red)){.hover\:text-primary\/80:hover{color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 80%, transparent)}}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-\[0_2px_6px_rgba\(0\,0\,0\,0\.04\)\,0_12px_40px_rgba\(0\,0\,0\,0\.10\)\]:hover{--tw-shadow:0 2px 6px var(--tw-shadow-color,#0000000a), 0 12px 40px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:border-primary:focus{border-color:var(--color-primary,oklch(50% .1 35))}.focus\:border-primary\/30:focus{border-color:#934c3a4d}@supports (color:color-mix(in lab, red, red)){.focus\:border-primary\/30:focus{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 30%, transparent)}}.focus\:bg-foreground:focus{background-color:var(--color-foreground,oklch(15% 0 0))}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary\/10:focus{--tw-ring-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/10:focus{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.focus\:ring-primary\/20:focus{--tw-ring-color:#934c3a33}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/20:focus{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 20%, transparent)}}.focus\:ring-ring:focus{--tw-ring-color:var(--color-ring,oklch(50% .1 35))}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-\[0\.99\]:active{scale:.99}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.aria-selected\:bg-foreground[aria-selected=true]{background-color:var(--color-foreground,oklch(15% 0 0))}.aria-selected\:text-background[aria-selected=true]{color:var(--color-background,oklch(99% 0 0))}@media (hover:hover){.aria-selected\:hover\:bg-foreground\/90[aria-selected=true]:hover{background-color:#0b0b0be6}@supports (color:color-mix(in lab, red, red)){.aria-selected\:hover\:bg-foreground\/90[aria-selected=true]:hover{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 90%, transparent)}}}.data-\[checked\]\:border-primary[data-checked]{border-color:var(--color-primary,oklch(50% .1 35))}.data-\[checked\]\:bg-primary\/5[data-checked]{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.data-\[checked\]\:bg-primary\/5[data-checked]{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}[open]>.\[\[open\]\>\&\]\:rotate-180{rotate:180deg}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-1\/2{top:50%}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[200\]{z-index:200}.z-\[9999\]{z-index:9999}.container{width:100%}.m-auto{margin:auto}.mx-auto{margin-inline:auto}.-mt-px{margin-top:-1px}.mr-auto{margin-right:auto}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[3\/4\]{aspect-ratio:3/4}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-\[5\/2\]{aspect-ratio:5/2}.aspect-\[16\/9\]{aspect-ratio:16/9}.aspect-square{aspect-ratio:1}.h-\[6px\]{height:6px}.h-\[7px\]{height:7px}.h-\[11px\]{height:11px}.h-\[18px\]{height:18px}.h-\[72px\]{height:72px}.h-\[min\(600px\,calc\(100vh-6rem\)\)\]{height:min(600px,100vh - 6rem)}.h-fit{height:fit-content}.h-full{height:100%}.max-h-\[85vh\]{max-height:85vh}.max-h-\[100px\]{max-height:100px}.min-h-\[20px\]{min-height:20px}.w-1\/2{width:50%}.w-2\/5{width:40%}.w-3\/4{width:75%}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-\[6px\]{width:6px}.w-\[7px\]{width:7px}.w-\[11px\]{width:11px}.w-\[18px\]{width:18px}.w-\[72px\]{width:72px}.w-\[400px\]{width:400px}.w-full{width:100%}.max-w-\[85\%\]{max-width:85%}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[300px\]{max-width:300px}.max-w-\[calc\(100vw-3rem\)\]{max-width:calc(100vw - 3rem)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.origin-bottom-left{transform-origin:0 100%}.origin-bottom-right{transform-origin:100% 100%}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize-none{resize:none}.list-none{list-style-type:none}.\[appearance\:textfield\]{appearance:textfield}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-border>:not(:last-child)){border-color:var(--color-border,oklch(90% 0 0))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:var(--radius,.5rem)}.rounded-\[10px\]{border-radius:10px}.rounded-\[14px\]{border-radius:14px}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-tl{border-top-left-radius:var(--radius,.5rem)}.rounded-tr{border-top-right-radius:var(--radius,.5rem)}.rounded-br{border-bottom-right-radius:var(--radius,.5rem)}.rounded-bl{border-bottom-left-radius:var(--radius,.5rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-\[1\.5px\]{border-style:var(--tw-border-style);border-width:1.5px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-border{border-color:var(--color-border,oklch(90% 0 0))}.border-foreground{border-color:var(--color-foreground,oklch(15% 0 0))}.border-input{border-color:var(--color-input,oklch(90% 0 0))}.border-muted-foreground\/30{border-color:#6363634d}@supports (color:color-mix(in lab, red, red)){.border-muted-foreground\/30{border-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.border-primary{border-color:var(--color-primary,oklch(50% .1 35))}.border-transparent{border-color:#0000}.border-t-foreground{border-top-color:var(--color-foreground,oklch(15% 0 0))}.bg-background{background-color:var(--color-background,oklch(99% 0 0))}.bg-background\/50{background-color:#fcfcfc80}@supports (color:color-mix(in lab, red, red)){.bg-background\/50{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 50%, transparent)}}.bg-background\/90{background-color:#fcfcfce6}@supports (color:color-mix(in lab, red, red)){.bg-background\/90{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 90%, transparent)}}.bg-background\/92{background-color:#fcfcfceb}@supports (color:color-mix(in lab, red, red)){.bg-background\/92{background-color:color-mix(in oklab, var(--color-background,oklch(99% 0 0)) 92%, transparent)}}.bg-border{background-color:var(--color-border,oklch(90% 0 0))}.bg-destructive{background-color:var(--color-destructive,oklch(50% .2 25))}.bg-destructive\/10{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.bg-foreground{background-color:var(--color-foreground,oklch(15% 0 0))}.bg-foreground\/40{background-color:#0b0b0b66}@supports (color:color-mix(in lab, red, red)){.bg-foreground\/40{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 40%, transparent)}}.bg-muted{background-color:var(--color-muted,oklch(95% 0 0))}.bg-muted-foreground\/40{background-color:#63636366}@supports (color:color-mix(in lab, red, red)){.bg-muted-foreground\/40{background-color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 40%, transparent)}}.bg-muted\/40{background-color:#eee6}@supports (color:color-mix(in lab, red, red)){.bg-muted\/40{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 40%, transparent)}}.bg-muted\/50{background-color:#eeeeee80}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 50%, transparent)}}.bg-primary{background-color:var(--color-primary,oklch(50% .1 35))}.bg-primary\/5{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.bg-primary\/5{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.bg-primary\/10{background-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.bg-primary\/10{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.bg-transparent{background-color:#0000}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.object-cover{object-fit:cover}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-\[inherit\]{font-family:inherit}.text-\[9px\]{font-size:9px}.text-\[10\.5px\]{font-size:10.5px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14\.5px\]{font-size:14.5px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[inherit\]{color:inherit}.text-background{color:var(--color-background,oklch(99% 0 0))}.text-border{color:var(--color-border,oklch(90% 0 0))}.text-destructive{color:var(--color-destructive,oklch(50% .2 25))}.text-destructive-foreground{color:var(--color-destructive-foreground,oklch(99% 0 0))}.text-foreground{color:var(--color-foreground,oklch(15% 0 0))}.text-foreground\/80{color:#0b0b0bcc}@supports (color:color-mix(in lab, red, red)){.text-foreground\/80{color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 80%, transparent)}}.text-muted-foreground{color:var(--color-muted-foreground,oklch(50% 0 0))}.text-muted-foreground\/30{color:#6363634d}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/30{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 30%, transparent)}}.text-muted-foreground\/40{color:#63636366}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/40{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 40%, transparent)}}.text-muted-foreground\/50{color:#63636380}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/50{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 50%, transparent)}}.text-muted-foreground\/60{color:#63636399}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/60{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 60%, transparent)}}.text-muted-foreground\/70{color:#636363b3}@supports (color:color-mix(in lab, red, red)){.text-muted-foreground\/70{color:color-mix(in oklab, var(--color-muted-foreground,oklch(50% 0 0)) 70%, transparent)}}.text-primary{color:var(--color-primary,oklch(50% .1 35))}.text-primary-foreground{color:var(--color-primary-foreground,oklch(99% 0 0))}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline{text-decoration-line:underline}.accent-foreground{accent-color:var(--color-foreground,oklch(15% 0 0))}.accent-primary{accent-color:var(--color-primary,oklch(50% .1 35))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-100{opacity:1}.shadow-\[0_1px_3px_rgba\(0\,0\,0\,0\.04\)\,0_6px_24px_rgba\(0\,0\,0\,0\.06\)\]{--tw-shadow:0 1px 3px var(--tw-shadow-color,#0000000a), 0 6px 24px var(--tw-shadow-color,#0000000f);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_1px_4px_rgba\(0\,0\,0\,0\.10\)\,0_4px_12px_rgba\(0\,0\,0\,0\.08\)\]{--tw-shadow:0 1px 4px var(--tw-shadow-color,#0000001a), 0 4px 12px var(--tw-shadow-color,#00000014);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-background{--tw-ring-color:var(--color-background,oklch(99% 0 0))}.ring-primary\/40{--tw-ring-color:#934c3a66}@supports (color:color-mix(in lab, red, red)){.ring-primary\/40{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 40%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-\[1px\]{--tw-backdrop-blur:blur(1px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-\[cubic-bezier\(0\.19\,1\,0\.22\,1\)\]{--tw-ease:cubic-bezier(.19,1,.22,1);transition-timing-function:cubic-bezier(.19,1,.22,1)}.\[transition-timing-function\:cubic-bezier\(0\.16\,1\,0\.3\,1\)\]{transition-timing-function:cubic-bezier(.16,1,.3,1)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.\[cimplify\:checkout\]{cimplify:checkout}.ring-inset{--tw-ring-inset:inset}@media (hover:hover){.group-hover\:scale-105:is(:where(.group):hover *){--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:scale-\[1\.04\]:is(:where(.group):hover *){scale:1.04}.group-hover\:text-foreground:is(:where(.group):hover *){color:var(--color-foreground,oklch(15% 0 0))}.group-hover\:text-primary:is(:where(.group):hover *){color:var(--color-primary,oklch(50% .1 35))}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-muted-foreground::placeholder{color:var(--color-muted-foreground,oklch(50% 0 0))}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.focus-within\:border-foreground:focus-within{border-color:var(--color-foreground,oklch(15% 0 0))}.focus-within\:bg-background:focus-within{background-color:var(--color-background,oklch(99% 0 0))}@media (hover:hover){.hover\:-translate-y-\[1px\]:hover{--tw-translate-y:calc(1px * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:-translate-y-px:hover{--tw-translate-y:-1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.hover\:\!scale-110:hover{--tw-scale-x:110%!important;--tw-scale-y:110%!important;--tw-scale-z:110%!important;scale:var(--tw-scale-x) var(--tw-scale-y)!important}.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.hover\:scale-\[1\.04\]:hover{scale:1.04}.hover\:scale-\[1\.08\]:hover{scale:1.08}.hover\:border-foreground:hover{border-color:var(--color-foreground,oklch(15% 0 0))}.hover\:border-foreground\/40:hover{border-color:#0b0b0b66}@supports (color:color-mix(in lab, red, red)){.hover\:border-foreground\/40:hover{border-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 40%, transparent)}}.hover\:border-primary\/20:hover{border-color:#934c3a33}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/20:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 20%, transparent)}}.hover\:border-primary\/40:hover{border-color:#934c3a66}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/40:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 40%, transparent)}}.hover\:border-primary\/50:hover{border-color:#934c3a80}@supports (color:color-mix(in lab, red, red)){.hover\:border-primary\/50:hover{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 50%, transparent)}}.hover\:\!bg-foreground:hover{background-color:var(--color-foreground,oklch(15% 0 0))!important}.hover\:bg-background:hover{background-color:var(--color-background,oklch(99% 0 0))}.hover\:bg-destructive\/10:hover{background-color:#bb061e1a}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/10:hover{background-color:color-mix(in oklab, var(--color-destructive,oklch(50% .2 25)) 10%, transparent)}}.hover\:bg-foreground\/90:hover{background-color:#0b0b0be6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-foreground\/90:hover{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 90%, transparent)}}.hover\:bg-muted:hover{background-color:var(--color-muted,oklch(95% 0 0))}.hover\:bg-muted\/40:hover{background-color:#eee6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/40:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 40%, transparent)}}.hover\:bg-muted\/70:hover{background-color:#eeeeeeb3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/70:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 70%, transparent)}}.hover\:bg-muted\/80:hover{background-color:#eeec}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/80:hover{background-color:color-mix(in oklab, var(--color-muted,oklch(95% 0 0)) 80%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#934c3ae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 90%, transparent)}}.hover\:\!text-background:hover{color:var(--color-background,oklch(99% 0 0))!important}.hover\:text-destructive:hover{color:var(--color-destructive,oklch(50% .2 25))}.hover\:text-foreground:hover{color:var(--color-foreground,oklch(15% 0 0))}.hover\:text-primary:hover{color:var(--color-primary,oklch(50% .1 35))}.hover\:text-primary\/80:hover{color:#934c3acc}@supports (color:color-mix(in lab, red, red)){.hover\:text-primary\/80:hover{color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 80%, transparent)}}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-\[0_2px_6px_rgba\(0\,0\,0\,0\.04\)\,0_12px_40px_rgba\(0\,0\,0\,0\.10\)\]:hover{--tw-shadow:0 2px 6px var(--tw-shadow-color,#0000000a), 0 12px 40px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.focus\:border-primary:focus{border-color:var(--color-primary,oklch(50% .1 35))}.focus\:border-primary\/30:focus{border-color:#934c3a4d}@supports (color:color-mix(in lab, red, red)){.focus\:border-primary\/30:focus{border-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 30%, transparent)}}.focus\:bg-foreground:focus{background-color:var(--color-foreground,oklch(15% 0 0))}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary\/10:focus{--tw-ring-color:#934c3a1a}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/10:focus{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 10%, transparent)}}.focus\:ring-primary\/20:focus{--tw-ring-color:#934c3a33}@supports (color:color-mix(in lab, red, red)){.focus\:ring-primary\/20:focus{--tw-ring-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 20%, transparent)}}.focus\:ring-ring:focus{--tw-ring-color:var(--color-ring,oklch(50% .1 35))}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring,oklch(50% .1 35))}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-\[0\.99\]:active{scale:.99}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.aria-selected\:bg-foreground[aria-selected=true]{background-color:var(--color-foreground,oklch(15% 0 0))}.aria-selected\:text-background[aria-selected=true]{color:var(--color-background,oklch(99% 0 0))}@media (hover:hover){.aria-selected\:hover\:bg-foreground\/90[aria-selected=true]:hover{background-color:#0b0b0be6}@supports (color:color-mix(in lab, red, red)){.aria-selected\:hover\:bg-foreground\/90[aria-selected=true]:hover{background-color:color-mix(in oklab, var(--color-foreground,oklch(15% 0 0)) 90%, transparent)}}}.data-\[checked\]\:border-primary[data-checked]{border-color:var(--color-primary,oklch(50% .1 35))}.data-\[checked\]\:bg-primary\/5[data-checked]{background-color:#934c3a0d}@supports (color:color-mix(in lab, red, red)){.data-\[checked\]\:bg-primary\/5[data-checked]{background-color:color-mix(in oklab, var(--color-primary,oklch(50% .1 35)) 5%, transparent)}}.data-\[fully-booked\]\:cursor-not-allowed[data-fully-booked]{cursor:not-allowed}.data-\[fully-booked\]\:opacity-40[data-fully-booked]{opacity:.4}.data-\[selected\]\:border-foreground[data-selected]{border-color:var(--color-foreground,oklch(15% 0 0))}.data-\[selected\]\:bg-foreground[data-selected]{background-color:var(--color-foreground,oklch(15% 0 0))}.data-\[selected\]\:text-background[data-selected]{color:var(--color-background,oklch(99% 0 0))}.data-\[unavailable\]\:cursor-not-allowed[data-unavailable]{cursor:not-allowed}.data-\[unavailable\]\:line-through[data-unavailable]{text-decoration-line:line-through}.data-\[unavailable\]\:opacity-40[data-unavailable]{opacity:.4}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}.\[\&\:\:-webkit-inner-spin-button\]\:appearance-none::-webkit-inner-spin-button{appearance:none}.\[\&\:\:-webkit-outer-spin-button\]\:appearance-none::-webkit-outer-spin-button{appearance:none}[open]>.\[\[open\]\>\&\]\:rotate-180{rotate:180deg}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "cards/compact-service-card.tsx",
|
|
13
|
-
"content": "\"use client\";\n\nimport React from \"react\";\nimport type { ServiceCardLayoutProps } from \"./standard-service-card\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nfunction formatDuration(minutes: number, unit?: string): string {\n if (unit && unit !== \"minutes\") return `${minutes} ${unit}`;\n if (minutes >= 60) {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return m > 0 ? `${h}h ${m}m` : `${h} hr`;\n }\n return `${minutes} min`;\n}\n\nexport function CompactServiceCard({\n product,\n renderImage,\n renderLink,\n className,\n}: ServiceCardLayoutProps): React.ReactElement {\n const image = product.image_url || product.images?.[0] || \"\";\n const hasDeposit = product.deposit_type && product.deposit_type !== \"none\" && product.deposit_amount;\n const hasBillingPlans = product.billing_plans && product.billing_plans.length > 0;\n const href = `/products/${product.slug}`;\n\n const content = (\n <div className=\"flex items-center gap-4 p-3\">\n {/* Thumbnail */}\n <div className=\"w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0\">\n {renderImage ? (\n renderImage({ src: image, alt: product.name, className: \"w-full h-full object-cover\" })\n ) : (\n <img src={image} alt={product.name} className=\"w-full h-full object-cover\" />\n )}\n </div>\n\n {/* Info */}\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-[14px] font-semibold text-foreground leading-tight truncate\">\n {product.name}\n </h3>\n {product.description && (\n <p className=\"text-[12px] text-muted-foreground mt-0.5 truncate\">\n {product.description}\n </p>\n )}\n <div className=\"flex items-center gap-2 mt-2 flex-wrap\">\n {product.duration_minutes != null && (\n <span className=\"text-[10.5px] font-medium text-muted-foreground px-1.5 py-0.5 bg-muted rounded\">\n {formatDuration(product.duration_minutes, product.duration_unit)}\n </span>\n )}\n <span className=\"flex items-center gap-1 text-[10.5px] font-medium text-emerald-600\">\n <span className=\"w-[6px] h-[6px] rounded-full bg-emerald-500\" />\n Available\n </span>\n {hasDeposit && (\n <span className=\"text-[10.5px] font-medium text-amber-600\">\n <Price amount={product.deposit_amount!} /> deposit\n </span>\n )}\n {hasBillingPlans && (\n <span className=\"inline-flex items-center gap-0.5 text-[10.5px] font-semibold text-primary px-1.5 py-0.5 bg-primary/10 rounded\">\n Subscription\n </span>\n )}\n </div>\n </div>\n\n {/* Price + Chevron */}\n <div className=\"flex items-center gap-2 shrink-0\">\n <Price amount={product.default_price} className=\"text-sm font-bold\" />\n <svg className=\"w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </div>\n </div>\n );\n\n const shellClass = cn(\n \"group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden\",\n \"shadow-[0_1px_3px_rgba(0,0,0,0.04),0_6px_24px_rgba(0,0,0,0.06)]\",\n \"border border-transparent\",\n \"transition-all duration-300 ease-[cubic-bezier(0.19,1,0.22,1)]\",\n \"hover:-translate-y-[1px] hover:shadow-[0_2px_6px_rgba(0,0,0,0.04),0_12px_40px_rgba(0,0,0,0.10)] hover:border-primary/20\",\n className,\n );\n\n if (renderLink) {\n return renderLink({ href, className: shellClass, children: content });\n }\n\n return <a href={href} className={shellClass} data-cimplify-card>{content}</a>;\n}\n"
|
|
13
|
+
"content": "\"use client\";\n\nimport React from \"react\";\nimport type { ServiceCardLayoutProps } from \"./standard-service-card\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { parsePrice } from \"@cimplify/sdk\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nfunction formatDuration(minutes: number, unit?: string): string {\n if (unit && unit !== \"minutes\") return `${minutes} ${unit}`;\n if (minutes >= 60) {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return m > 0 ? `${h}h ${m}m` : `${h} hr`;\n }\n return `${minutes} min`;\n}\n\nexport function CompactServiceCard({\n product,\n renderImage,\n renderLink,\n className,\n}: ServiceCardLayoutProps): React.ReactElement {\n const image = product.image_url || product.images?.[0] || \"\";\n const hasDeposit = product.deposit_type && product.deposit_type !== \"none\" && product.deposit_amount;\n const hasBillingPlans = product.billing_plans && product.billing_plans.length > 0;\n const href = `/products/${product.slug}`;\n\n const content = (\n <div className=\"flex items-center gap-4 p-3\">\n {/* Thumbnail */}\n <div className=\"w-[72px] h-[72px] rounded-xl overflow-hidden bg-muted shrink-0\">\n {renderImage ? (\n renderImage({ src: image, alt: product.name, className: \"w-full h-full object-cover\" })\n ) : image ? (\n <img src={image} alt={product.name} className=\"w-full h-full object-cover\" />\n ) : null}\n </div>\n\n {/* Info */}\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-[14px] font-semibold text-foreground leading-tight truncate\">\n {product.name}\n </h3>\n {product.description && (\n <p className=\"text-[12px] text-muted-foreground mt-0.5 truncate\">\n {product.description}\n </p>\n )}\n <div className=\"flex items-center gap-2 mt-2 flex-wrap\">\n {product.duration_minutes != null && (\n <span className=\"text-[10.5px] font-medium text-muted-foreground px-1.5 py-0.5 bg-muted rounded\">\n {formatDuration(product.duration_minutes, product.duration_unit)}\n </span>\n )}\n <span className=\"flex items-center gap-1 text-[10.5px] font-medium text-emerald-600\">\n <span className=\"w-[6px] h-[6px] rounded-full bg-emerald-500\" />\n Available\n </span>\n {hasDeposit && (\n <span className=\"text-[10.5px] font-medium text-amber-600\">\n <Price amount={product.deposit_amount!} /> deposit\n </span>\n )}\n {hasBillingPlans && (\n <span className=\"inline-flex items-center gap-0.5 text-[10.5px] font-semibold text-primary px-1.5 py-0.5 bg-primary/10 rounded\">\n Subscription\n </span>\n )}\n </div>\n </div>\n\n {/* Price + Chevron */}\n <div className=\"flex items-center gap-2 shrink-0\">\n <Price amount={product.default_price} className=\"text-sm font-bold\" />\n <svg className=\"w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </div>\n </div>\n );\n\n const shellClass = cn(\n \"group block text-left cursor-pointer bg-card rounded-[14px] overflow-hidden\",\n \"shadow-[0_1px_3px_rgba(0,0,0,0.04),0_6px_24px_rgba(0,0,0,0.06)]\",\n \"border border-transparent\",\n \"transition-all duration-300 ease-[cubic-bezier(0.19,1,0.22,1)]\",\n \"hover:-translate-y-[1px] hover:shadow-[0_2px_6px_rgba(0,0,0,0.04),0_12px_40px_rgba(0,0,0,0.10)] hover:border-primary/20\",\n className,\n );\n\n if (renderLink) {\n return renderLink({ href, className: shellClass, children: content });\n }\n\n return <a href={href} className={shellClass} data-cimplify-card>{content}</a>;\n}\n"
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "date-slot-picker.tsx",
|
|
13
|
-
"content": "\"use client\";\n\nimport React, { useState, useMemo, useCallback } from \"react\";\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport type { AvailableSlot, DayAvailability } from \"@cimplify/sdk\";\nimport type { DurationUnit, SchedulingMode } from \"@cimplify/sdk\";\nimport { useServiceAvailability } from \"@cimplify/sdk/react\";\nimport { SlotPicker } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface DateSlotPickerClassNames {\n root?: string;\n dateStrip?: string;\n dateButton?: string;\n nav?: string;\n navButton?: string;\n slots?: string;\n loading?: string;\n}\n\nexport interface DateSlotPickerProps {\n /** Service ID to fetch availability and slots for. */\n serviceId: string;\n /** Number of days to show in the date strip. Default: 7. */\n daysToShow?: number;\n /** Number of participants. */\n participantCount?: number;\n /** Currently selected slot. */\n selectedSlot?: AvailableSlot | null;\n /** Called when a slot is selected. */\n onSlotSelect?: (slot: AvailableSlot, date: string) => void;\n /** Pre-fetched availability data (skips fetch). */\n availability?: DayAvailability[];\n /** Show price on slots. Default: true. */\n showPrice?: boolean;\n /** Forwarded to `<SlotPicker>` to render multi-day stay labels. */\n schedulingMode?: SchedulingMode;\n /** Forwarded to `<SlotPicker>` — unit for the stay summary in multi-day mode. */\n durationUnit?: DurationUnit;\n /** Forwarded to `<SlotPicker>` — value for the stay summary in multi-day mode. */\n durationValue?: number;\n className?: string;\n classNames?: DateSlotPickerClassNames;\n}\n\nfunction formatDate(dateStr: string): string {\n const date = new Date(dateStr + \"T00:00:00\");\n return date.toLocaleDateString(undefined, { weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0];\n}\n\nfunction addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\nexport function DateSlotPicker({\n serviceId,\n daysToShow = 7,\n participantCount,\n selectedSlot,\n onSlotSelect,\n availability: availabilityProp,\n showPrice = true,\n schedulingMode,\n durationUnit,\n durationValue,\n className,\n classNames,\n}: DateSlotPickerProps): React.ReactElement {\n const [offset, setOffset] = useState(0);\n const [selectedDate, setSelectedDate] = useState<string>(toDateString(new Date()));\n\n const dateRange = useMemo(() => {\n const today = new Date();\n const start = addDays(today, offset);\n const dates: string[] = [];\n for (let i = 0; i < daysToShow; i++) {\n dates.push(toDateString(addDays(start, i)));\n }\n return {\n dates,\n startDate: dates[0],\n endDate: dates[dates.length - 1],\n };\n }, [offset, daysToShow]);\n\n const { days: fetchedDays, isLoading: availabilityLoading } = useServiceAvailability(\n serviceId,\n dateRange.startDate,\n dateRange.endDate,\n {\n participantCount,\n enabled: availabilityProp === undefined,\n },\n );\n\n const days = availabilityProp ?? fetchedDays;\n\n const availabilityMap = useMemo(() => {\n const map = new Map<string, DayAvailability>();\n for (const day of days) {\n map.set(day.date, day);\n }\n return map;\n }, [days]);\n\n const handlePrev = useCallback(() => {\n setOffset((prev) => Math.max(0, prev - daysToShow));\n }, [daysToShow]);\n\n const handleNext = useCallback(() => {\n setOffset((prev) => prev + daysToShow);\n }, [daysToShow]);\n\n const handleDateChange = useCallback((value: string | number | null) => {\n if (typeof value === \"string\") {\n setSelectedDate(value);\n }\n }, []);\n\n const handleSlotSelect = useCallback(\n (slot: AvailableSlot) => {\n onSlotSelect?.(slot, selectedDate);\n },\n [onSlotSelect, selectedDate],\n );\n\n return (\n <Tabs.Root\n value={selectedDate}\n onValueChange={handleDateChange}\n data-cimplify-date-slot-picker\n className={cn(className, classNames?.root)}\n >\n <div
|
|
13
|
+
"content": "\"use client\";\n\nimport React, { useState, useMemo, useCallback } from \"react\";\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport type { AvailableSlot, DayAvailability } from \"@cimplify/sdk\";\nimport type { DurationUnit, SchedulingMode } from \"@cimplify/sdk\";\nimport { useServiceAvailability } from \"@cimplify/sdk/react\";\nimport { SlotPicker } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface DateSlotPickerClassNames {\n root?: string;\n dateStrip?: string;\n dateButton?: string;\n nav?: string;\n navButton?: string;\n slots?: string;\n loading?: string;\n}\n\nexport interface DateSlotPickerProps {\n /** Service ID to fetch availability and slots for. */\n serviceId: string;\n /** Number of days to show in the date strip. Default: 7. */\n daysToShow?: number;\n /** Number of participants. */\n participantCount?: number;\n /** Currently selected slot. */\n selectedSlot?: AvailableSlot | null;\n /** Called when a slot is selected. */\n onSlotSelect?: (slot: AvailableSlot, date: string) => void;\n /** Pre-fetched availability data (skips fetch). */\n availability?: DayAvailability[];\n /** Show price on slots. Default: true. */\n showPrice?: boolean;\n /** Forwarded to `<SlotPicker>` to render multi-day stay labels. */\n schedulingMode?: SchedulingMode;\n /** Forwarded to `<SlotPicker>` — unit for the stay summary in multi-day mode. */\n durationUnit?: DurationUnit;\n /** Forwarded to `<SlotPicker>` — value for the stay summary in multi-day mode. */\n durationValue?: number;\n className?: string;\n classNames?: DateSlotPickerClassNames;\n}\n\nfunction formatDate(dateStr: string): string {\n const date = new Date(dateStr + \"T00:00:00\");\n return date.toLocaleDateString(undefined, { weekday: \"short\", month: \"short\", day: \"numeric\" });\n}\n\nfunction toDateString(date: Date): string {\n return date.toISOString().split(\"T\")[0];\n}\n\nfunction addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\nexport function DateSlotPicker({\n serviceId,\n daysToShow = 7,\n participantCount,\n selectedSlot,\n onSlotSelect,\n availability: availabilityProp,\n showPrice = true,\n schedulingMode,\n durationUnit,\n durationValue,\n className,\n classNames,\n}: DateSlotPickerProps): React.ReactElement {\n const [offset, setOffset] = useState(0);\n const [selectedDate, setSelectedDate] = useState<string>(toDateString(new Date()));\n\n const dateRange = useMemo(() => {\n const today = new Date();\n const start = addDays(today, offset);\n const dates: string[] = [];\n for (let i = 0; i < daysToShow; i++) {\n dates.push(toDateString(addDays(start, i)));\n }\n return {\n dates,\n startDate: dates[0],\n endDate: dates[dates.length - 1],\n };\n }, [offset, daysToShow]);\n\n const { days: fetchedDays, isLoading: availabilityLoading } = useServiceAvailability(\n serviceId,\n dateRange.startDate,\n dateRange.endDate,\n {\n participantCount,\n enabled: availabilityProp === undefined,\n },\n );\n\n const days = availabilityProp ?? fetchedDays;\n\n const availabilityMap = useMemo(() => {\n const map = new Map<string, DayAvailability>();\n for (const day of days) {\n map.set(day.date, day);\n }\n return map;\n }, [days]);\n\n const handlePrev = useCallback(() => {\n setOffset((prev) => Math.max(0, prev - daysToShow));\n }, [daysToShow]);\n\n const handleNext = useCallback(() => {\n setOffset((prev) => prev + daysToShow);\n }, [daysToShow]);\n\n const handleDateChange = useCallback((value: string | number | null) => {\n if (typeof value === \"string\") {\n setSelectedDate(value);\n }\n }, []);\n\n const handleSlotSelect = useCallback(\n (slot: AvailableSlot) => {\n onSlotSelect?.(slot, selectedDate);\n },\n [onSlotSelect, selectedDate],\n );\n\n return (\n <Tabs.Root\n value={selectedDate}\n onValueChange={handleDateChange}\n data-cimplify-date-slot-picker\n className={cn(\"flex flex-col gap-4\", className, classNames?.root)}\n >\n <div\n data-cimplify-date-nav\n className={cn(\"flex items-center justify-end gap-2\", classNames?.nav)}\n >\n <button\n type=\"button\"\n onClick={handlePrev}\n disabled={offset === 0}\n aria-label=\"Previous dates\"\n data-cimplify-date-nav-prev\n className={cn(\n \"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40\",\n classNames?.navButton,\n )}\n >\n ←\n </button>\n <button\n type=\"button\"\n onClick={handleNext}\n aria-label=\"Next dates\"\n data-cimplify-date-nav-next\n className={cn(\n \"grid place-items-center w-8 h-8 rounded-md border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n classNames?.navButton,\n )}\n >\n →\n </button>\n </div>\n\n <Tabs.List\n data-cimplify-date-strip\n className={cn(\"grid grid-cols-7 gap-1 sm:gap-2\", classNames?.dateStrip)}\n >\n {dateRange.dates.map((date) => {\n const dayInfo = availabilityMap.get(date);\n const hasAvailability = dayInfo?.has_availability !== false;\n const isSelected = selectedDate === date;\n return (\n <Tabs.Tab\n key={date}\n value={date}\n data-cimplify-date-button\n data-selected={isSelected || undefined}\n data-available={hasAvailability || undefined}\n data-fully-booked={(!hasAvailability) || undefined}\n className={cn(\n \"flex flex-col items-center justify-center rounded-md border border-border bg-background px-1 py-2 text-center text-xs font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[fully-booked]:cursor-not-allowed data-[fully-booked]:opacity-40\",\n classNames?.dateButton,\n )}\n >\n {formatDate(date)}\n </Tabs.Tab>\n );\n })}\n </Tabs.List>\n\n {availabilityLoading && (\n <div\n data-cimplify-date-slot-loading\n aria-busy=\"true\"\n className={cn(\"h-32 rounded-md bg-muted/40 animate-pulse\", classNames?.loading)}\n />\n )}\n\n <div data-cimplify-date-slots className={classNames?.slots}>\n <SlotPicker\n serviceId={serviceId}\n date={selectedDate}\n participantCount={participantCount}\n selectedSlot={selectedSlot}\n onSlotSelect={handleSlotSelect}\n showPrice={showPrice}\n schedulingMode={schedulingMode}\n durationUnit={durationUnit}\n durationValue={durationValue}\n />\n </div>\n </Tabs.Root>\n );\n}\n"
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "product-sheet.tsx",
|
|
15
|
-
"content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport type { Product, ProductWithDetails, VariantView } from \"@cimplify/sdk\";\nimport type { AddToCartOptions } from \"@cimplify/sdk/react\";\nimport { useProduct } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { ProductImageGallery } from \"@cimplify/sdk/react\";\nimport { ProductCustomizer } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface ProductSheetClassNames {\n root?: string;\n image?: string;\n header?: string;\n name?: string;\n price?: string;\n description?: string;\n customizer?: string;\n loading?: string;\n}\n\nexport interface ProductSheetProps {\n /** A slim Product (triggers lazy-fetch) or a fully-loaded ProductWithDetails. */\n product: Product | ProductWithDetails;\n /** Called when the sheet should close. */\n onClose?: () => void;\n /** Override the default add-to-cart behavior. */\n onAddToCart?: (\n product: ProductWithDetails,\n quantity: number,\n options: AddToCartOptions,\n ) => void | Promise<void>;\n /** Custom image renderer (e.g. Next.js Image). */\n renderImage?: (props: {\n src: string;\n alt: string;\n className?: string;\n }) => React.ReactNode;\n className?: string;\n classNames?: ProductSheetClassNames;\n}\n\nfunction isProductWithDetails(\n product: Product | ProductWithDetails,\n): product is ProductWithDetails {\n return \"variants\" in product;\n}\n\n/**\n * ProductSheet — full product detail view composing gallery, header, and customizer.\n *\n * When given a slim `Product`, it lazy-fetches the full details via `useProduct`.\n * When given a `ProductWithDetails`, it skips the fetch entirely.\n */\nexport function ProductSheet({\n product,\n onClose,\n onAddToCart,\n renderImage,\n className,\n classNames,\n}: ProductSheetProps): React.ReactElement {\n const needsFetch = !isProductWithDetails(product);\n const { product: fetched, isLoading } = useProduct(\n product.slug ?? product.id,\n { enabled: needsFetch },\n );\n const fullProduct = needsFetch ? fetched : (product as ProductWithDetails);\n\n // Loading state\n if (isLoading && !fullProduct) {\n return (\n <div\n data-cimplify-product-sheet\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n >\n <div\n data-cimplify-product-sheet-skeleton\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <div\n style={{\n aspectRatio: \"4/3\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.5rem\",\n }}\n />\n <div\n style={{\n height: \"1.5rem\",\n width: \"60%\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.25rem\",\n }}\n />\n <div\n style={{\n height: \"1rem\",\n width: \"30%\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.25rem\",\n }}\n />\n </div>\n </div>\n );\n }\n\n // Error state\n if (!fullProduct) {\n return (\n <div\n data-cimplify-product-sheet\n className={cn(className, classNames?.root)}\n >\n <p>Product not found.</p>\n </div>\n );\n }\n\n const [selectedVariant, setSelectedVariant] = useState<VariantView | undefined>(\n undefined,\n );\n\n const variantImages = selectedVariant?.images?.filter(Boolean) ?? [];\n const productImages: string[] = [];\n if (fullProduct.images && fullProduct.images.length > 0) {\n productImages.push(...fullProduct.images.filter(Boolean));\n } else if (fullProduct.image_url) {\n productImages.push(fullProduct.image_url);\n }\n const images: string[] =\n variantImages.length > 0 ? variantImages : productImages;\n\n const hasMultipleImages = images.length > 1;\n const singleImage = images[0];\n\n return (\n <div\n data-cimplify-product-sheet\n className={cn(className, classNames?.root)}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}\n >\n {/* Image area */}\n {hasMultipleImages ? (\n <ProductImageGallery\n images={images}\n productName={fullProduct.name}\n className={classNames?.image}\n />\n ) : singleImage ? (\n <div data-cimplify-product-sheet-image className={classNames?.image}>\n {renderImage ? (\n renderImage({ src: singleImage, alt: fullProduct.name })\n ) : (\n <img\n src={singleImage}\n alt={fullProduct.name}\n style={{ width: \"100%\", height: \"auto\", objectFit: \"cover\" }}\n />\n )}\n </div>\n ) : null}\n\n {/* Header */}\n <div data-cimplify-product-sheet-header className={classNames?.header}>\n <h2\n data-cimplify-product-sheet-name\n className={classNames?.name}\n style={{ margin: 0 }}\n >\n {fullProduct.name}\n </h2>\n <Price\n amount={fullProduct.default_price}\n className={classNames?.price}\n />\n </div>\n\n {/* Description */}\n {fullProduct.description && (\n <
|
|
15
|
+
"content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport type { Product, ProductWithDetails, VariantView } from \"@cimplify/sdk\";\nimport type { AddToCartOptions } from \"@cimplify/sdk/react\";\nimport { useProduct } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { ProductImageGallery } from \"@cimplify/sdk/react\";\nimport { ProductCustomizer } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface ProductSheetClassNames {\n root?: string;\n image?: string;\n header?: string;\n name?: string;\n price?: string;\n description?: string;\n customizer?: string;\n loading?: string;\n}\n\nexport interface ProductSheetProps {\n /** A slim Product (triggers lazy-fetch) or a fully-loaded ProductWithDetails. */\n product: Product | ProductWithDetails;\n /** Called when the sheet should close. */\n onClose?: () => void;\n /** Override the default add-to-cart behavior. */\n onAddToCart?: (\n product: ProductWithDetails,\n quantity: number,\n options: AddToCartOptions,\n ) => void | Promise<void>;\n /** Custom image renderer (e.g. Next.js Image). */\n renderImage?: (props: {\n src: string;\n alt: string;\n className?: string;\n }) => React.ReactNode;\n className?: string;\n classNames?: ProductSheetClassNames;\n}\n\nfunction isProductWithDetails(\n product: Product | ProductWithDetails,\n): product is ProductWithDetails {\n return \"variants\" in product;\n}\n\n/**\n * ProductSheet — full product detail view composing gallery, header, and customizer.\n *\n * When given a slim `Product`, it lazy-fetches the full details via `useProduct`.\n * When given a `ProductWithDetails`, it skips the fetch entirely.\n */\nexport function ProductSheet({\n product,\n onClose,\n onAddToCart,\n renderImage,\n className,\n classNames,\n}: ProductSheetProps): React.ReactElement {\n const needsFetch = !isProductWithDetails(product);\n const { product: fetched, isLoading } = useProduct(\n product.slug ?? product.id,\n { enabled: needsFetch },\n );\n const fullProduct = needsFetch ? fetched : (product as ProductWithDetails);\n\n // Loading state\n if (isLoading && !fullProduct) {\n return (\n <div\n data-cimplify-product-sheet\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n >\n <div\n data-cimplify-product-sheet-skeleton\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"1rem\",\n }}\n >\n <div\n style={{\n aspectRatio: \"4/3\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.5rem\",\n }}\n />\n <div\n style={{\n height: \"1.5rem\",\n width: \"60%\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.25rem\",\n }}\n />\n <div\n style={{\n height: \"1rem\",\n width: \"30%\",\n backgroundColor: \"rgba(0,0,0,0.06)\",\n borderRadius: \"0.25rem\",\n }}\n />\n </div>\n </div>\n );\n }\n\n // Error state\n if (!fullProduct) {\n return (\n <div\n data-cimplify-product-sheet\n className={cn(className, classNames?.root)}\n >\n <p>Product not found.</p>\n </div>\n );\n }\n\n const [selectedVariant, setSelectedVariant] = useState<VariantView | undefined>(\n undefined,\n );\n\n const variantImages = selectedVariant?.images?.filter(Boolean) ?? [];\n const productImages: string[] = [];\n if (fullProduct.images && fullProduct.images.length > 0) {\n productImages.push(...fullProduct.images.filter(Boolean));\n } else if (fullProduct.image_url) {\n productImages.push(fullProduct.image_url);\n }\n const images: string[] =\n variantImages.length > 0 ? variantImages : productImages;\n\n const hasMultipleImages = images.length > 1;\n const singleImage = images[0];\n\n return (\n <div\n data-cimplify-product-sheet\n className={cn(className, classNames?.root)}\n style={{ display: \"flex\", flexDirection: \"column\", gap: \"1rem\" }}\n >\n {/* Image area */}\n {hasMultipleImages ? (\n <ProductImageGallery\n images={images}\n productName={fullProduct.name}\n className={classNames?.image}\n />\n ) : singleImage ? (\n <div data-cimplify-product-sheet-image className={classNames?.image}>\n {renderImage ? (\n renderImage({ src: singleImage, alt: fullProduct.name })\n ) : (\n <img\n src={singleImage}\n alt={fullProduct.name}\n style={{ width: \"100%\", height: \"auto\", objectFit: \"cover\" }}\n />\n )}\n </div>\n ) : null}\n\n {/* Header */}\n <div data-cimplify-product-sheet-header className={classNames?.header}>\n <h2\n data-cimplify-product-sheet-name\n className={classNames?.name}\n style={{ margin: 0 }}\n >\n {fullProduct.name}\n </h2>\n <Price\n amount={fullProduct.default_price}\n className={classNames?.price}\n />\n </div>\n\n {/* Description — merchant-authored HTML from the catalogue. */}\n {fullProduct.description && (\n <div\n data-cimplify-product-sheet-description\n className={cn(\"text-sm leading-relaxed text-muted-foreground [&_p]:m-0 [&_p+p]:mt-2\", classNames?.description)}\n dangerouslySetInnerHTML={{ __html: fullProduct.description }}\n />\n )}\n\n {/* Customizer */}\n <ProductCustomizer\n product={fullProduct}\n onAddToCart={onAddToCart}\n onVariantChange={(_id, variant) => setSelectedVariant(variant)}\n className={classNames?.customizer}\n />\n </div>\n );\n}\n"
|
|
16
16
|
}
|
|
17
17
|
]
|
|
18
18
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
{
|
|
12
12
|
"path": "slot-picker.tsx",
|
|
13
|
-
"content": "\"use client\";\n\nimport { Radio } from \"@base-ui/react/radio\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport React from \"react\";\nimport type { AvailableSlot } from \"@cimplify/sdk\";\nimport type { DurationUnit, SchedulingMode } from \"@cimplify/sdk\";\nimport { useAvailableSlots } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface SlotPickerClassNames {\n root?: string;\n group?: string;\n groupLabel?: string;\n slot?: string;\n slotTime?: string;\n slotPrice?: string;\n loading?: string;\n empty?: string;\n}\n\nexport interface SlotPickerProps {\n /** Pre-fetched slots (skips fetch). */\n slots?: AvailableSlot[];\n /** Service ID — used to fetch slots when `slots` prop is not provided. */\n serviceId?: string;\n /** Date string (YYYY-MM-DD) — used to fetch slots when `slots` prop is not provided. */\n date?: string;\n /** Number of participants for capacity-based availability. */\n participantCount?: number;\n /** Currently selected slot. */\n selectedSlot?: AvailableSlot | null;\n /** Called when a slot is selected. */\n onSlotSelect?: (slot: AvailableSlot) => void;\n /** Whether to group slots by time of day. Default: true. Ignored when `schedulingMode` is `\"multi_day\"`. */\n groupByTimeOfDay?: boolean;\n /** Show price on each slot. Default: true. */\n showPrice?: boolean;\n /**\n * Service scheduling mode. When `\"multi_day\"`, each slot renders as a\n * stay summary (`\"3 nights: Fri Apr 5, 3:00 PM → Mon Apr 8, 11:00 AM\"`)\n * instead of the time-of-day label. Defaults to `\"intraday\"`.\n */\n schedulingMode?: SchedulingMode;\n /** Service duration unit — used for the stay summary in multi-day mode. */\n durationUnit?: DurationUnit;\n /** Service duration value — used for the stay summary in multi-day mode. */\n durationValue?: number;\n /** Text shown when no slots available. */\n emptyMessage?: string;\n className?: string;\n classNames?: SlotPickerClassNames;\n}\n\ninterface SlotGroup {\n label: string;\n slots: AvailableSlot[];\n}\n\nfunction getTimeOfDay(timeStr: string): \"morning\" | \"afternoon\" | \"evening\" {\n const hour = parseInt(timeStr.split(\"T\").pop()?.split(\":\")[0] ?? timeStr.split(\":\")[0], 10);\n if (hour < 12) return \"morning\";\n if (hour < 17) return \"afternoon\";\n return \"evening\";\n}\n\nconst TIME_OF_DAY_LABELS: Record<string, string> = {\n morning: \"Morning\",\n afternoon: \"Afternoon\",\n evening: \"Evening\",\n};\n\nfunction groupSlots(slots: AvailableSlot[]): SlotGroup[] {\n const groups: Record<string, AvailableSlot[]> = {};\n for (const slot of slots) {\n const tod = getTimeOfDay(slot.start_time);\n if (!groups[tod]) groups[tod] = [];\n groups[tod].push(slot);\n }\n return ([\"morning\", \"afternoon\", \"evening\"] as const)\n .filter((tod) => groups[tod]?.length)\n .map((tod) => ({ label: TIME_OF_DAY_LABELS[tod], slots: groups[tod] }));\n}\n\nfunction formatTime(timeStr: string): string {\n try {\n const date = new Date(timeStr);\n if (!isNaN(date.getTime())) {\n return date.toLocaleTimeString(undefined, { hour: \"numeric\", minute: \"2-digit\" });\n }\n } catch {\n // noop\n }\n\n const parts = timeStr.split(\":\");\n if (parts.length >= 2) {\n const hour = parseInt(parts[0], 10);\n const minute = parts[1];\n const ampm = hour >= 12 ? \"PM\" : \"AM\";\n const displayHour = hour % 12 || 12;\n return `${displayHour}:${minute} ${ampm}`;\n }\n return timeStr;\n}\n\nfunction pluralizeUnit(unit: DurationUnit | undefined, value: number | undefined): string {\n if (!unit) return value === 1 ? \"day\" : \"days\";\n const v = value ?? 1;\n if (unit === \"minutes\") return v === 1 ? \"minute\" : \"minutes\";\n if (unit === \"hours\") return v === 1 ? \"hour\" : \"hours\";\n if (unit === \"days\") return v === 1 ? \"day\" : \"days\";\n if (unit === \"weeks\") return v === 1 ? \"week\" : \"weeks\";\n if (unit === \"months\") return v === 1 ? \"month\" : \"months\";\n return unit;\n}\n\nfunction formatStaySummary(\n slot: AvailableSlot,\n durationUnit: DurationUnit | undefined,\n durationValue: number | undefined,\n): string {\n const start = new Date(slot.start_time);\n const end = new Date(slot.end_time);\n const startLabel = start.toLocaleString(undefined, {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endLabel = end.toLocaleString(undefined, {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const unitLabel = pluralizeUnit(durationUnit, durationValue);\n if (durationValue !== undefined) {\n return `${durationValue} ${unitLabel}: ${startLabel} → ${endLabel}`;\n }\n return `${startLabel} → ${endLabel}`;\n}\n\nfunction slotToValue(slot: AvailableSlot): string {\n return `${slot.start_time}|${slot.end_time}`;\n}\n\nexport function SlotPicker({\n slots: slotsProp,\n serviceId,\n date,\n participantCount,\n selectedSlot,\n onSlotSelect,\n groupByTimeOfDay = true,\n showPrice = true,\n schedulingMode = \"intraday\",\n durationUnit,\n durationValue,\n emptyMessage = \"No available slots\",\n className,\n classNames,\n}: SlotPickerProps): React.ReactElement {\n const isMultiDay = schedulingMode === \"multi_day\";\n const { slots: fetched, isLoading } = useAvailableSlots(\n serviceId ?? null,\n date ?? null,\n {\n participantCount,\n enabled: slotsProp === undefined && !!serviceId && !!date,\n },\n );\n\n const slots = slotsProp ?? fetched;\n\n if (isLoading && slots.length === 0) {\n return (\n <div\n data-cimplify-slot-picker\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (slots.length === 0) {\n return (\n <div\n data-cimplify-slot-picker\n data-empty\n className={cn(className, classNames?.root, classNames?.empty)}\n >\n <p>{emptyMessage}</p>\n </div>\n );\n }\n\n const groups = groupByTimeOfDay && !isMultiDay\n ? groupSlots(slots)\n : [{ label: \"\", slots }];\n\n const slotsByValue = new Map<string, AvailableSlot>();\n for (const slot of slots) {\n slotsByValue.set(slotToValue(slot), slot);\n }\n\n const selectedValue = selectedSlot ? slotToValue(selectedSlot) : \"\";\n\n return (\n <RadioGroup\n data-cimplify-slot-picker\n className={cn(className, classNames?.root)}\n value={selectedValue}\n onValueChange={(value: string) => {\n const slot = slotsByValue.get(value);\n if (slot
|
|
13
|
+
"content": "\"use client\";\n\nimport { Radio } from \"@base-ui/react/radio\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport React from \"react\";\nimport type { AvailableSlot } from \"@cimplify/sdk\";\nimport type { DurationUnit, SchedulingMode } from \"@cimplify/sdk\";\nimport { useAvailableSlots } from \"@cimplify/sdk/react\";\nimport { Price } from \"@cimplify/sdk/react\";\nimport { cn } from \"@cimplify/sdk/react\";\n\nexport interface SlotPickerClassNames {\n root?: string;\n group?: string;\n groupLabel?: string;\n slot?: string;\n slotTime?: string;\n slotPrice?: string;\n loading?: string;\n empty?: string;\n}\n\nexport interface SlotPickerProps {\n /** Pre-fetched slots (skips fetch). */\n slots?: AvailableSlot[];\n /** Service ID — used to fetch slots when `slots` prop is not provided. */\n serviceId?: string;\n /** Date string (YYYY-MM-DD) — used to fetch slots when `slots` prop is not provided. */\n date?: string;\n /** Number of participants for capacity-based availability. */\n participantCount?: number;\n /** Currently selected slot. */\n selectedSlot?: AvailableSlot | null;\n /** Called when a slot is selected. */\n onSlotSelect?: (slot: AvailableSlot) => void;\n /** Whether to group slots by time of day. Default: true. Ignored when `schedulingMode` is `\"multi_day\"`. */\n groupByTimeOfDay?: boolean;\n /** Show price on each slot. Default: true. */\n showPrice?: boolean;\n /**\n * Service scheduling mode. When `\"multi_day\"`, each slot renders as a\n * stay summary (`\"3 nights: Fri Apr 5, 3:00 PM → Mon Apr 8, 11:00 AM\"`)\n * instead of the time-of-day label. Defaults to `\"intraday\"`.\n */\n schedulingMode?: SchedulingMode;\n /** Service duration unit — used for the stay summary in multi-day mode. */\n durationUnit?: DurationUnit;\n /** Service duration value — used for the stay summary in multi-day mode. */\n durationValue?: number;\n /** Text shown when no slots available. */\n emptyMessage?: string;\n className?: string;\n classNames?: SlotPickerClassNames;\n}\n\ninterface SlotGroup {\n label: string;\n slots: AvailableSlot[];\n}\n\nfunction getTimeOfDay(timeStr: string): \"morning\" | \"afternoon\" | \"evening\" {\n const hour = parseInt(timeStr.split(\"T\").pop()?.split(\":\")[0] ?? timeStr.split(\":\")[0], 10);\n if (hour < 12) return \"morning\";\n if (hour < 17) return \"afternoon\";\n return \"evening\";\n}\n\nconst TIME_OF_DAY_LABELS: Record<string, string> = {\n morning: \"Morning\",\n afternoon: \"Afternoon\",\n evening: \"Evening\",\n};\n\nfunction groupSlots(slots: AvailableSlot[]): SlotGroup[] {\n const groups: Record<string, AvailableSlot[]> = {};\n for (const slot of slots) {\n const tod = getTimeOfDay(slot.start_time);\n if (!groups[tod]) groups[tod] = [];\n groups[tod].push(slot);\n }\n return ([\"morning\", \"afternoon\", \"evening\"] as const)\n .filter((tod) => groups[tod]?.length)\n .map((tod) => ({ label: TIME_OF_DAY_LABELS[tod], slots: groups[tod] }));\n}\n\nfunction formatTime(timeStr: string): string {\n try {\n const date = new Date(timeStr);\n if (!isNaN(date.getTime())) {\n return date.toLocaleTimeString(undefined, { hour: \"numeric\", minute: \"2-digit\" });\n }\n } catch {\n // noop\n }\n\n const parts = timeStr.split(\":\");\n if (parts.length >= 2) {\n const hour = parseInt(parts[0], 10);\n const minute = parts[1];\n const ampm = hour >= 12 ? \"PM\" : \"AM\";\n const displayHour = hour % 12 || 12;\n return `${displayHour}:${minute} ${ampm}`;\n }\n return timeStr;\n}\n\nfunction pluralizeUnit(unit: DurationUnit | undefined, value: number | undefined): string {\n if (!unit) return value === 1 ? \"day\" : \"days\";\n const v = value ?? 1;\n if (unit === \"minutes\") return v === 1 ? \"minute\" : \"minutes\";\n if (unit === \"hours\") return v === 1 ? \"hour\" : \"hours\";\n if (unit === \"days\") return v === 1 ? \"day\" : \"days\";\n if (unit === \"weeks\") return v === 1 ? \"week\" : \"weeks\";\n if (unit === \"months\") return v === 1 ? \"month\" : \"months\";\n return unit;\n}\n\nfunction formatStaySummary(\n slot: AvailableSlot,\n durationUnit: DurationUnit | undefined,\n durationValue: number | undefined,\n): string {\n const start = new Date(slot.start_time);\n const end = new Date(slot.end_time);\n const startLabel = start.toLocaleString(undefined, {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const endLabel = end.toLocaleString(undefined, {\n weekday: \"short\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n const unitLabel = pluralizeUnit(durationUnit, durationValue);\n if (durationValue !== undefined) {\n return `${durationValue} ${unitLabel}: ${startLabel} → ${endLabel}`;\n }\n return `${startLabel} → ${endLabel}`;\n}\n\nfunction slotToValue(slot: AvailableSlot): string {\n return `${slot.start_time}|${slot.end_time}`;\n}\n\nexport function SlotPicker({\n slots: slotsProp,\n serviceId,\n date,\n participantCount,\n selectedSlot,\n onSlotSelect,\n groupByTimeOfDay = true,\n showPrice = true,\n schedulingMode = \"intraday\",\n durationUnit,\n durationValue,\n emptyMessage = \"No available slots\",\n className,\n classNames,\n}: SlotPickerProps): React.ReactElement {\n const isMultiDay = schedulingMode === \"multi_day\";\n const { slots: fetched, isLoading } = useAvailableSlots(\n serviceId ?? null,\n date ?? null,\n {\n participantCount,\n enabled: slotsProp === undefined && !!serviceId && !!date,\n },\n );\n\n const slots = slotsProp ?? fetched;\n\n if (isLoading && slots.length === 0) {\n return (\n <div\n data-cimplify-slot-picker\n aria-busy=\"true\"\n className={cn(className, classNames?.root, classNames?.loading)}\n />\n );\n }\n\n if (slots.length === 0) {\n return (\n <div\n data-cimplify-slot-picker\n data-empty\n className={cn(className, classNames?.root, classNames?.empty)}\n >\n <p>{emptyMessage}</p>\n </div>\n );\n }\n\n const groups = groupByTimeOfDay && !isMultiDay\n ? groupSlots(slots)\n : [{ label: \"\", slots }];\n\n const slotsByValue = new Map<string, AvailableSlot>();\n for (const slot of slots) {\n slotsByValue.set(slotToValue(slot), slot);\n }\n\n const selectedValue = selectedSlot ? slotToValue(selectedSlot) : \"\";\n\n return (\n <RadioGroup\n data-cimplify-slot-picker\n className={cn(\"flex flex-col gap-4\", className, classNames?.root)}\n value={selectedValue}\n onValueChange={(value: string) => {\n const slot = slotsByValue.get(value);\n // Slots default to available; treat as unavailable only when the\n // backend explicitly returns `is_available: false`.\n if (slot && slot.is_available !== false) {\n onSlotSelect?.(slot);\n }\n }}\n >\n {groups.map((group) => (\n <div\n key={group.label || \"all\"}\n data-cimplify-slot-group\n className={cn(\"flex flex-col gap-2\", classNames?.group)}\n >\n {group.label && (\n <div\n data-cimplify-slot-group-label\n className={cn(\n \"text-xs font-medium uppercase tracking-[0.12em] text-muted-foreground\",\n classNames?.groupLabel,\n )}\n >\n {group.label}\n </div>\n )}\n <div\n className={cn(\n isMultiDay\n ? \"flex flex-col gap-2\"\n : \"grid grid-cols-3 sm:grid-cols-4 gap-2\",\n )}\n >\n {group.slots.map((slot) => {\n const value = slotToValue(slot);\n const isSelected =\n selectedSlot?.start_time === slot.start_time &&\n selectedSlot?.end_time === slot.end_time;\n return (\n <Radio.Root\n key={value}\n value={value}\n disabled={slot.is_available === false}\n data-cimplify-slot\n data-selected={isSelected || undefined}\n data-unavailable={slot.is_available === false || undefined}\n className={cn(\n \"inline-flex items-center justify-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:border-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[selected]:border-foreground data-[selected]:bg-foreground data-[selected]:text-background data-[unavailable]:cursor-not-allowed data-[unavailable]:opacity-40 data-[unavailable]:line-through\",\n isMultiDay && \"justify-between text-left\",\n classNames?.slot,\n )}\n >\n <span data-cimplify-slot-time className={classNames?.slotTime}>\n {isMultiDay\n ? formatStaySummary(slot, durationUnit, durationValue)\n : formatTime(slot.start_time)}\n </span>\n {showPrice && slot.price && (\n <span\n data-cimplify-slot-price\n className={cn(\"text-xs opacity-70\", classNames?.slotPrice)}\n >\n <Price amount={slot.price} />\n </span>\n )}\n </Radio.Root>\n );\n })}\n </div>\n </div>\n ))}\n </RadioGroup>\n );\n}\n"
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
}
|