"Add to Calendar" removed from the staff appointment details page — it never showed for owner/customer views anyway, only here. Buttons now stack in one column, in exactly the order you asked: Check In → Reschedule → Mark No-Show → Cancel. Reschedule moved from a separate top row into appointment-status-actions.tsx itself (new rescheduleUrl prop) so it can sit in the right spot relative to the status-specific buttons — this also means it correctly disappears for statuses that were never reschedulable (e.g. once In Progress), same as before. Add Loyalty Stamp is now an icon-only button (add-stamp-button.tsx gained an iconOnly prop) sitting directly opposite "X/Y stamps" inside the card — same click-to-open-modal behavior, just compact.
This commit is contained in:
3 files changed
+52
-46
No files matched your search
@@ -14,11 +14,14 @@ export function AddStampButton({
|
||||
appointmentId,
|
||||
className,
|
||||
size = "sm",
|
||||
iconOnly = false,
|
||||
}: {
|
||||
customerId: string;
|
||||
appointmentId?: string;
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg";
|
||||
/** Renders just the stamp icon (e.g. opposite a "X/Y stamps" line) instead of icon + label — same action, just compact enough to sit inline with other text. */
|
||||
iconOnly?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -64,12 +67,13 @@ export function AddStampButton({
|
||||
<>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size={size}
|
||||
size={iconOnly ? "icon" : size}
|
||||
className={cn("shrink-0", className)}
|
||||
onClick={() => setOpen(true)}
|
||||
aria-label={iconOnly ? "Add loyalty stamp" : undefined}
|
||||
>
|
||||
<Stamp className="h-4 w-4" aria-hidden="true" />
|
||||
Add Loyalty Stamp
|
||||
{!iconOnly && "Add Loyalty Stamp"}
|
||||
</Button>
|
||||
<Modal open={open} onClose={close} title={result ? "Stamp Added" : "Add Loyalty Stamp"}>
|
||||
{result ? (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -55,12 +56,15 @@ export function AppointmentStatusActions({
|
||||
status,
|
||||
actions = DEFAULT_ACTIONS,
|
||||
layout = "wrap",
|
||||
rescheduleUrl,
|
||||
}: {
|
||||
appointmentId: string;
|
||||
status: RawStatus;
|
||||
actions?: AppointmentActionSet;
|
||||
/** "stack" renders one full-width button per row (Current Appointment); "wrap" (default, used by Appointment Details) keeps the original inline small buttons. */
|
||||
/** "stack" renders one full-width button per row (used by both Current Appointment and Appointment Details); "wrap" (default) keeps the original inline small buttons. */
|
||||
layout?: "wrap" | "stack";
|
||||
/** When given, renders a "Reschedule" link right after the primary status button and before Mark No-Show/Cancel — the caller (Appointment Details) decides whether the status is actually reschedulable, this component just renders what it's given. */
|
||||
rescheduleUrl?: string;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
@@ -169,6 +173,11 @@ export function AppointmentStatusActions({
|
||||
Complete Appointment
|
||||
</Button>
|
||||
)}
|
||||
{rescheduleUrl && (
|
||||
<Button asChild size={buttonSize} className={buttonClassName} variant="secondary">
|
||||
<Link href={rescheduleUrl}>Reschedule</Link>
|
||||
</Button>
|
||||
)}
|
||||
{status === "CONFIRMED" && (
|
||||
<Button size={buttonSize} className={buttonClassName} variant="secondary" onClick={() => setConfirmKind("no_show")}>
|
||||
Mark No-Show
|
||||
|
||||
Reference in new issue
Block a user