# Datatable 18 - Hover Preview Appointments Table

> A clinic appointments table with search, an add appointment dialog, row actions, and a cancel confirmation. Hovering a row opens a preview card with the visit details. Useful for healthcare and booking dashboards.

A shadcn/ui block from the [@shadcn-dashboard](https://shadcndashboard.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcn-dashboard/datatable-18).

- Page, with a live preview: https://shadcn.exchange/r/shadcn-dashboard/datatable-18
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcn-dashboard/datatable-18-light-813a2b1dd41e0ac1.png
- Registry item: https://shadcndashboard.dev/r/datatable-18.json
- Listed: 2026-10-20, source last changed 2026-10-20

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/shadcn-dashboard/datatable-18.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`
- Registry items: `alert-dialog`, `avatar`, `badge`, `button`, `card`, `dialog`, `dropdown-menu`, `hover-card`, `input`, `input-group`, `label`, `select`, `table`

## Files

### `src/components/shadcn-dashboard/blocks/datatable/datatable-18/page.tsx`

```tsx
import HoverTable from "@/components/shadcn-dashboard/blocks/datatable/datatable-18/datatable"

const page = () => {
  return <HoverTable />
}

export default page
```

### `src/components/shadcn-dashboard/blocks/datatable/datatable-18/datatable.tsx`

```tsx
"use client";

import { useMemo, useState } from "react";
import {
  BanIcon,
  CircleCheckIcon,
  MoreVerticalIcon,
  PlusIcon,
  SearchIcon,
  StethoscopeIcon,
  UserCheckIcon,
} from "lucide-react";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Card,
  CardAction,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Input } from "@/components/ui/input";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";

type AppointmentStatus =
  | "Scheduled"
  | "Waiting"
  | "In Consultation"
  | "Completed"
  | "Cancelled";

type Appointment = {
  id: string;
  patient: {
    name: string;
    avatar: string;
    age: number;
    gender: string;
    phone: string;
  };
  doctor: { name: string; specialty: string };
  room: string;
  type: string;
  time: string;
  endTime: string;
  status: AppointmentStatus;
  /** short line shown under the time */
  when: string;
  /** 0 to 100, how much of the visit is done */
  progress: number;
  note?: string;
};

const AVATAR =
  "https://images.shadcnspace.com/assets/shadcn-dashboard/profile/user-";

const initialAppointments: Appointment[] = [
  {
    id: "AP-3101", room: "Room 204", type: "Check-up", time: "08:30", endTime: "09:00",
    status: "Completed", when: "Finished 09:00", progress: 100,
    patient: { name: "Naomi Fischer", avatar: `${AVATAR}2.webp`, age: 34, gender: "Female", phone: "+1 206 555 0134" },
    doctor: { name: "Dr. Meera Patel", specialty: "Cardiology" },
  },
  {
    id: "AP-3102", room: "Room 112", type: "Follow-up", time: "09:15", endTime: "10:00",
    status: "In Consultation", when: "In progress", progress: 50,
    patient: { name: "Tobias Lindqvist", avatar: `${AVATAR}3.webp`, age: 29, gender: "Male", phone: "+1 503 555 0171" },
    doctor: { name: "Dr. Samuel Okafor", specialty: "Orthopedics" },
    note: "Doctor is running 15 minutes behind schedule.",
  },
  {
    id: "AP-3103", room: "Room 305", type: "Consultation", time: "09:45", endTime: "10:15",
    status: "Waiting", when: "In waiting room", progress: 0,
    patient: { name: "Amara Diallo", avatar: `${AVATAR}6.webp`, age: 27, gender: "Female", phone: "+1 312 555 0188" },
    doctor: { name: "Dr. Linh Nguyen", specialty: "Dermatology" },
  },
  {
    id: "AP-3104", room: "Room 204", type: "Lab review", time: "10:30", endTime: "10:50",
    status: "Cancelled", when: "Cancelled", progress: 0,
    patient: { name: "Yuki Hashimoto", avatar: `${AVATAR}1.webp`, age: 36, gender: "Male", phone: "+1 415 555 0126" },
    doctor: { name: "Dr. Meera Patel", specialty: "Cardiology" },
    note: "Patient request: rescheduled to Oct 21.",
  },
  {
    id: "AP-3105", room: "Room 112", type: "Check-up", time: "11:15", endTime: "11:35",
    status: "Scheduled", when: "Scheduled", progress: 0,
    patient: { name: "Mateo Alvarez", avatar: `${AVATAR}7.webp`, age: 24, gender: "Male", phone: "+1 305 555 0149" },
    doctor: { name: "Dr. Samuel Okafor", specialty: "Orthopedics" },
  },
  {
    id: "AP-3106", room: "Room 112", type: "Consultation", time: "13:00", endTime: "14:00",
    status: "Scheduled", when: "Scheduled", progress: 0,
    patient: { name: "Elias Solberg", avatar: `${AVATAR}5.webp`, age: 42, gender: "Male", phone: "+1 612 555 0163" },
    doctor: { name: "Dr. Samuel Okafor", specialty: "Orthopedics" },
  },
];

const doctors = [
  { name: "Dr. Meera Patel", specialty: "Cardiology", room: "Room 204" },
  { name: "Dr. Samuel Okafor", specialty: "Orthopedics", room: "Room 112" },
  { name: "Dr. Linh Nguyen", specialty: "Dermatology", room: "Room 305" },
  { name: "Dr. Anna Brandt", specialty: "Pediatrics", room: "Room 101" },
];

const visitTypes = ["Check-up", "Follow-up", "Consultation", "Lab review", "Vaccination"];
const genders = ["Female", "Male", "Other"];
const cancelReasons = ["Patient request", "Doctor unavailable", "No show", "Other"];

const STATUS_STYLES: Record<AppointmentStatus, string> = {
  Scheduled: "bg-sky-400/10! text-sky-400!",
  Waiting: "bg-orange-400/10! text-orange-400!",
  "In Consultation": "bg-chart-2/10! text-chart-2!",
  Completed: "bg-primary/10! text-primary!",
  Cancelled: "bg-destructive/10! text-destructive!",
};

const SEGMENTS = 20;
const VISIT_LENGTH = 30;

const pad = (n: number) => String(n).padStart(2, "0");
const addMinutes = (time: string, minutes: number) => {
  const [h, m] = time.split(":").map(Number);
  const total = h * 60 + m + minutes;
  return `${pad(Math.floor(total / 60) % 24)}:${pad(total % 60)}`;
};

const emptyForm = {
  name: "",
  age: "",
  gender: "Female",
  phone: "",
  doctor: doctors[0].name,
  type: "Check-up",
  time: "",
};

function AppointmentPreview({ item }: { item: Appointment }) {
  const filled = Math.round((item.progress / 100) * SEGMENTS);
  const facts = [
    { label: "Doctor", value: `${item.doctor.name}, ${item.doctor.specialty}` },
    { label: "Visit type", value: item.type },
    { label: "Room", value: item.room },
    { label: "Time", value: `${item.time} to ${item.endTime}` },
    { label: "Phone", value: item.patient.phone || "Not provided" },
  ];

  return (
    <div className="flex flex-col gap-3">
      <div className="flex items-center justify-between gap-3">
        <div className="flex items-center gap-3">
          <Avatar>
            {item.patient.avatar && (
              <AvatarImage src={item.patient.avatar} alt={item.patient.name} />
            )}
            <AvatarFallback>{item.patient.name[0]}</AvatarFallback>
          </Avatar>
          <div className="flex flex-col">
            <h6 className="text-sm font-medium">{item.patient.name}</h6>
            <span className="text-xs text-muted-foreground">
              {item.id} &middot; {item.patient.age}, {item.patient.gender}
            </span>
          </div>
        </div>
        <Badge className={STATUS_STYLES[item.status]}>{item.status}</Badge>
      </div>

      <div className="flex flex-col gap-2 border-t border-border pt-3 text-sm">
        {facts.map((f) => (
          <div key={f.label} className="flex items-center justify-between gap-4">
            <span className="text-muted-foreground">{f.label}</span>
            <span className="text-right">{f.value}</span>
          </div>
        ))}
      </div>

      <div className="flex flex-col gap-2 border-t border-border pt-3">
        <div className="flex items-center justify-between text-sm">
          <span className="text-muted-foreground">Consultation</span>
          <span className="text-xs text-muted-foreground">{item.progress}% done</span>
        </div>
        <div className="flex gap-0.5">
          {Array.from({ length: SEGMENTS }).map((_, i) => (
            <span
              key={i}
              className={cn("h-2 flex-1", i < filled ? "bg-primary" : "bg-muted")}
            />
          ))}
        </div>
      </div>

      {item.note && (
        <p className="border-t border-border pt-3 text-xs text-muted-foreground">
          {item.note}
        </p>
      )}
    </div>
  );
}

function HoverTable() {
  const [items, setItems] = useState<Appointment[]>(initialAppointments);
  const [search, setSearch] = useState("");
  const [addOpen, setAddOpen] = useState(false);
  const [form, setForm] = useState(emptyForm);
  const [cancelTarget, setCancelTarget] = useState<Appointment | null>(null);
  const [cancelReason, setCancelReason] = useState(cancelReasons[0]);
  const [cancelNote, setCancelNote] = useState("");

  const rows = useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return items;
    return items.filter((a) =>
      [a.id, a.patient.name, a.doctor.name, a.doctor.specialty, a.type, a.room].some((v) =>
        v.toLowerCase().includes(q),
      ),
    );
  }, [items, search]);

  const update = (id: string, patch: Partial<Appointment>) =>
    setItems((prev) => prev.map((a) => (a.id === id ? { ...a, ...patch } : a)));

  const checkIn = (a: Appointment) =>
    update(a.id, { status: "Waiting", when: "In waiting room", progress: 0 });

  const startConsultation = (a: Appointment) =>
    update(a.id, { status: "In Consultation", when: "In progress", progress: 10 });

  const complete = (a: Appointment) =>
    update(a.id, { status: "Completed", when: `Finished ${a.endTime}`, progress: 100 });

  const openCancel = (a: Appointment) => {
    setCancelReason(cancelReasons[0]);
    setCancelNote("");
    setCancelTarget(a);
  };

  const confirmCancel = () => {
    if (!cancelTarget) return;
    const extra = cancelNote.trim();
    update(cancelTarget.id, {
      status: "Cancelled",
      when: "Cancelled",
      progress: 0,
      note: extra ? `${cancelReason}: ${extra}` : cancelReason,
    });
    setCancelTarget(null);
  };

  const addAppointment = () => {
    const doctor = doctors.find((d) => d.name === form.doctor) ?? doctors[0];
    const next: Appointment = {
      id: `AP-${3100 + items.length + 1}`,
      room: doctor.room,
      type: form.type,
      time: form.time,
      endTime: addMinutes(form.time, VISIT_LENGTH),
      status: "Scheduled",
      when: "Scheduled",
      progress: 0,
      patient: {
        name: form.name.trim(),
        avatar: "",
        age: Number(form.age) || 0,
        gender: form.gender,
        phone: form.phone.trim(),
      },
      doctor: { name: doctor.name, specialty: doctor.specialty },
    };
    setItems((prev) => [...prev, next].sort((a, b) => a.time.localeCompare(b.time)));
    setForm(emptyForm);
    setAddOpen(false);
  };

  return (
    <section className="py-8 px-4 max-w-4xl w-full mx-auto">
      <Card className="w-full py-0 gap-0 ring-0 border border-border">
        <CardHeader className="flex items-center justify-between flex-wrap gap-4 p-3.5">
          <div>
            <CardTitle>Appointments</CardTitle>
            <CardDescription>
              Hover over a row to preview the appointment details
            </CardDescription>
          </div>
          <CardAction className="flex flex-wrap items-center gap-2">
            <InputGroup className="w-56 dark:bg-background dark:border-border">
              <InputGroupAddon align="inline-start">
                <SearchIcon className="size-4" />
              </InputGroupAddon>
              <InputGroupInput
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                placeholder="Search patients..."
              />
            </InputGroup>

            <Dialog open={addOpen} onOpenChange={setAddOpen}>
              <DialogTrigger
                render={
                  <Button className="cursor-pointer" variant="outline">
                    <PlusIcon className="size-4" />
                    Add appointment
                  </Button>
                }
              />
              <DialogContent>
                <DialogHeader>
                  <DialogTitle>Add appointment</DialogTitle>
                  <DialogDescription>
                    Book a new visit for a patient.
                  </DialogDescription>
                </DialogHeader>
                <div className="grid gap-4">
                  <div className="grid gap-1.5">
                    <Label htmlFor="appt-name">Patient name</Label>
                    <Input
                      id="appt-name"
                      placeholder="Enter patient name"
                      value={form.name}
                      onChange={(e) => setForm({ ...form, name: e.target.value })}
                    />
                  </div>
                  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
                    <div className="grid gap-1.5">
                      <Label htmlFor="appt-age">Age</Label>
                      <Input
                        id="appt-age"
                        type="number"
                        min={0}
                        placeholder="Age"
                        value={form.age}
                        onChange={(e) => setForm({ ...form, age: e.target.value })}
                      />
                    </div>
                    <div className="grid gap-1.5">
                      <Label>Gender</Label>
                      <Select
                        value={form.gender}
                        onValueChange={(v) => setForm({ ...form, gender: v ?? "Female" })}
                      >
                        <SelectTrigger className="w-full cursor-pointer">
                          <SelectValue />
                        </SelectTrigger>
                        <SelectContent>
                          {genders.map((g) => (
                            <SelectItem className="cursor-pointer" key={g} value={g}>
                              {g}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                    <div className="grid gap-1.5">
                      <Label>Doctor</Label>
                      <Select
                        value={form.doctor}
                        onValueChange={(v) => setForm({ ...form, doctor: v ?? doctors[0].name })}
                      >
                        <SelectTrigger className="w-full cursor-pointer">
                          <SelectValue />
                        </SelectTrigger>
                        <SelectContent>
                          {doctors.map((d) => (
                            <SelectItem className="cursor-pointer" key={d.name} value={d.name}>
                              {d.name}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                    <div className="grid gap-1.5">
                      <Label>Visit type</Label>
                      <Select
                        value={form.type}
                        onValueChange={(v) => setForm({ ...form, type: v ?? "Check-up" })}
                      >
                        <SelectTrigger className="w-full cursor-pointer">
                          <SelectValue />
                        </SelectTrigger>
                        <SelectContent>
                          {visitTypes.map((t) => (
                            <SelectItem className="cursor-pointer" key={t} value={t}>
                              {t}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                    <div className="grid gap-1.5">
                      <Label htmlFor="appt-time">Time</Label>
                      <Input
                        id="appt-time"
                        type="time"
                        value={form.time}
                        onChange={(e) => setForm({ ...form, time: e.target.value })}
                      />
                    </div>
                    <div className="grid gap-1.5">
                      <Label htmlFor="appt-phone">Phone (optional)</Label>
                      <Input
                        id="appt-phone"
                        placeholder="+1 000 000 0000"
                        value={form.phone}
                        onChange={(e) => setForm({ ...form, phone: e.target.value })}
                      />
                    </div>
                  </div>
                </div>
                <DialogFooter>
                  <Button
                    className="cursor-pointer"
                    disabled={!form.name.trim() || !form.time}
                    onClick={addAppointment}
                  >
                    Add appointment
                  </Button>
                </DialogFooter>
              </DialogContent>
            </Dialog>
          </CardAction>
        </CardHeader>

        <div className="border-t border-border">
          <Table className="min-w-full">
            <TableHeader>
              <TableRow>
                <TableHead className="text-base font-medium py-3 text-left border-b border-border px-4 h-auto">
                  Patient
                </TableHead>
                <TableHead className="text-base font-medium py-3 text-left border-b border-border px-4 h-auto">
                  Doctor
                </TableHead>
                <TableHead className="text-base font-medium py-3 text-left border-b border-border px-4 h-auto">
                  Time
                </TableHead>
                <TableHead className="text-base font-medium py-3 text-left border-b border-border px-4 h-auto">
                  Status
                </TableHead>
                <TableHead className="text-base font-medium py-3 text-left border-b border-border px-4 h-auto">
                  Room
                </TableHead>
                <TableHead className="w-12 border-b border-border px-4 h-auto" />
              </TableRow>
            </TableHeader>
            <TableBody className="divide-y divide-border">
              {rows.length === 0 && (
                <TableRow>
                  <TableCell
                    colSpan={6}
                    className="h-24 text-center text-muted-foreground"
                  >
                    No appointments found.
                  </TableCell>
                </TableRow>
              )}
              {rows.map((item) => {
                const canCancel = item.status === "Scheduled" || item.status === "Waiting";
                const hasActions =
                  item.status === "Scheduled" ||
                  item.status === "Waiting" ||
                  item.status === "In Consultation";
                return (
                  <HoverCard key={item.id}>
                    <HoverCardTrigger
                      delay={150}
                      closeDelay={0}
                      render={
                        <TableRow className="cursor-default hover:bg-muted/50">
                          <TableCell className="whitespace-nowrap py-3 px-4">
                            <div className="flex items-center gap-3">
                              <Avatar>
                                {item.patient.avatar && (
                                  <AvatarImage
                                    src={item.patient.avatar}
                                    alt={item.patient.name}
                                  />
                                )}
                                <AvatarFallback>{item.patient.name[0]}</AvatarFallback>
                              </Avatar>
                              <div className="flex flex-col">
                                <h6 className="text-sm font-medium">{item.patient.name}</h6>
                                <span className="text-xs text-muted-foreground">
                                  {item.patient.age}, {item.patient.gender}
                                </span>
                              </div>
                            </div>
                          </TableCell>
                          <TableCell className="whitespace-nowrap py-3 px-4">
                            <div className="flex flex-col">
                              <h6 className="text-sm font-medium">{item.doctor.name}</h6>
                              <span className="text-xs text-muted-foreground">
                                {item.doctor.specialty}
                              </span>
                            </div>
                          </TableCell>
                          <TableCell className="whitespace-nowrap py-3 px-4">
                            <div className="flex flex-col">
                              <h6 className="text-sm font-medium">{item.time}</h6>
                              <span className="text-xs text-muted-foreground">
                                {item.when}
                              </span>
                            </div>
                          </TableCell>
                          <TableCell className="whitespace-nowrap py-3 px-4">
                            <Badge className={STATUS_STYLES[item.status]}>
                              {item.status}
                            </Badge>
                          </TableCell>
                          <TableCell className="whitespace-nowrap py-3 px-4">
                            {item.room}
                          </TableCell>
                          <TableCell className="py-3 px-4">
                            <div className="flex items-center justify-end gap-1">
                              <DropdownMenu>
                                <DropdownMenuTrigger
                                  render={
                                    <Button
                                      variant="ghost"
                                      size="icon-sm"
                                      aria-label={`Actions for ${item.patient.name}`}
                                      className="cursor-pointer"
                                    >
                                      <MoreVerticalIcon className="size-4" />
                                    </Button>
                                  }
                                />
                                <DropdownMenuContent align="end" className="w-48">
                                  <DropdownMenuGroup>
                                    <DropdownMenuLabel>Actions</DropdownMenuLabel>
                                  </DropdownMenuGroup>
                                  <DropdownMenuSeparator />
                                  {!hasActions && (
                                    <DropdownMenuItem disabled>
                                      No actions available
                                    </DropdownMenuItem>
                                  )}
                                  {item.status === "Scheduled" && (
                                    <DropdownMenuItem
                                      className="cursor-pointer"
                                      onClick={() => checkIn(item)}
                                    >
                                      <UserCheckIcon /> Check in
                                    </DropdownMenuItem>
                                  )}
                                  {item.status === "Waiting" && (
                                    <DropdownMenuItem
                                      className="cursor-pointer"
                                      onClick={() => startConsultation(item)}
                                    >
                                      <StethoscopeIcon /> Start consultation
                                    </DropdownMenuItem>
                                  )}
                                  {item.status === "In Consultation" && (
                                    <DropdownMenuItem
                                      className="cursor-pointer"
                                      onClick={() => complete(item)}
                                    >
                                      <CircleCheckIcon /> Complete
                                    </DropdownMenuItem>
                                  )}
                                  {canCancel && (
                                    <>
                                      <DropdownMenuSeparator />
                                      <DropdownMenuItem
                                        className="cursor-pointer text-destructive"
                                        onClick={() => openCancel(item)}
                                      >
                                        <BanIcon /> Cancel appointment
                                      </DropdownMenuItem>
                                    </>
                                  )}
                                </DropdownMenuContent>
                              </DropdownMenu>
                            </div>
                          </TableCell>
                        </TableRow>
                      }
                    />
                    <HoverCardContent
                      side="bottom"
                      align="center"
                      sideOffset={4}
                      className="pointer-events-none w-80 p-4 transition-[opacity,transform] duration-200 ease-out data-starting-style:translate-y-1 data-starting-style:opacity-0 data-ending-style:opacity-0"
                    >
                      <AppointmentPreview item={item} />
                    </HoverCardContent>
                  </HoverCard>
                );
              })}
            </TableBody>
          </Table>
        </div>
      </Card>

      <AlertDialog
        open={cancelTarget !== null}
        onOpenChange={(open) => {
          if (!open) setCancelTarget(null);
        }}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Cancel appointment?</AlertDialogTitle>
            <AlertDialogDescription>
              {cancelTarget && (
                <>
                  This will cancel{" "}
                  <span className="font-medium text-foreground">
                    {cancelTarget.patient.name}
                  </span>
                  &apos;s visit at {cancelTarget.time} with {cancelTarget.doctor.name}.
                </>
              )}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <div className="grid gap-4">
            <div className="grid gap-1.5">
              <Label>Reason</Label>
              <Select
                value={cancelReason}
                onValueChange={(v) => setCancelReason(v ?? cancelReasons[0])}
              >
                <SelectTrigger className="w-full cursor-pointer">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {cancelReasons.map((r) => (
                    <SelectItem className="cursor-pointer" key={r} value={r}>
                      {r}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div className="grid gap-1.5">
              <Label htmlFor="cancel-note">Note (optional)</Label>
              <Input
                id="cancel-note"
                placeholder="Add a short note"
                value={cancelNote}
                onChange={(e) => setCancelNote(e.target.value)}
              />
            </div>
          </div>
          <AlertDialogFooter>
            <AlertDialogCancel className="cursor-pointer">Keep appointment</AlertDialogCancel>
            <AlertDialogAction
              className="bg-destructive text-white hover:bg-destructive/90 cursor-pointer"
              onClick={confirmCancel}
            >
              Cancel appointment
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </section>
  );
}

export default HoverTable;
```
