Skip to content

Commit

Permalink
chore(room-booking): add delete confirmation dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
ArtemSBulgakov committed Oct 26, 2024
1 parent c891614 commit 825ed43
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 36 deletions.
88 changes: 52 additions & 36 deletions src/components/room-booking/list/BookingsListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { useMe } from "@/api/accounts/user.ts";
import { $roomBooking, roomBookingTypes } from "@/api/room-booking";
import { SignInButton } from "@/components/common/SignInButton.tsx";
import Tooltip from "@/components/common/Tooltip.tsx";
import { DeleteBookingModal } from "@/components/room-booking/list/DeleteBookingModal.tsx";
import { clockTime, durationFormatted, msBetween } from "@/lib/utils/dates.ts";
import { useQueryClient } from "@tanstack/react-query";
import clsx from "clsx";
import React, { useMemo } from "react";
import React, { useMemo, useState } from "react";

export function BookingsListPage() {
const { me } = useMe();
Expand Down Expand Up @@ -61,6 +62,8 @@ export function BookingCard({
const { data: rooms } = $roomBooking.useQuery("get", "/rooms/");
const room = rooms?.find((v) => v.id === booking.room_id);

const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);

const { mutate: deleteBookingMutate, isPending } = $roomBooking.useMutation(
"delete",
"/bookings/{booking_id}",
Expand All @@ -82,41 +85,54 @@ export function BookingCard({
const end = useMemo(() => new Date(booking.end), [booking.end]);

return (
<div className="group flex flex-row rounded-lg border-[1px] border-primary-hover bg-primary-main px-4 py-2">
<div className="flex grow flex-col">
<h2 className="text-gray-900 dark:text-gray-100">{booking.title}</h2>
<p className="text-base text-gray-600 dark:text-gray-400">
<span>{room?.title}</span>
<span className="text-gray-400 dark:text-gray-700"></span>
<span>
{start.toLocaleString("en-US", { day: "numeric", month: "short" })},{" "}
{start.toLocaleString("en-US", { weekday: "short" })}
</span>
<span className="text-gray-400 dark:text-gray-700"></span>
<span>
{`${clockTime(start)}${clockTime(end)} (${durationFormatted(msBetween(start, end))})`}
</span>
</p>
</div>
<div
className={clsx(
"invisible flex flex-row items-center gap-2 group-hover:visible",
isPending && "visible",
)}
>
<Tooltip content="Delete booking">
<button
className="flex h-8 w-8 items-center justify-center rounded-md border-[1px] border-red-400 bg-red-200 text-red-600 hover:bg-red-300 dark:border-red-600 dark:bg-red-800 dark:text-red-400 dark:hover:bg-red-700"
onClick={() => deleteBooking()}
>
{!isPending ? (
<span className="icon-[material-symbols--close-rounded] text-2xl" />
) : (
<span className="icon-[mdi--loading] animate-spin text-2xl text-icon-main" />
)}
</button>
</Tooltip>
<>
<div className="group flex flex-row rounded-lg border-[1px] border-primary-hover bg-primary-main px-4 py-2">
<div className="flex grow flex-col">
<h2 className="text-gray-900 dark:text-gray-100">{booking.title}</h2>
<p className="text-base text-gray-600 dark:text-gray-400">
<span>{room?.title}</span>
<span className="text-gray-400 dark:text-gray-700"></span>
<span>
{start.toLocaleString("en-US", {
day: "numeric",
month: "short",
})}
, {start.toLocaleString("en-US", { weekday: "short" })}
</span>
<span className="text-gray-400 dark:text-gray-700"></span>
<span>
{`${clockTime(start)}${clockTime(end)} (${durationFormatted(msBetween(start, end))})`}
</span>
</p>
</div>
<div
className={clsx(
"flex flex-row items-center gap-2 group-hover:visible",
isPending ? "visible" : "invisible",
)}
>
<Tooltip content="Delete booking">
<button
className="flex h-8 w-8 items-center justify-center rounded-md border-[1px] border-red-400 bg-red-200 text-red-600 hover:bg-red-300 dark:border-red-600 dark:bg-red-800 dark:text-red-400 dark:hover:bg-red-700"
onClick={() => setConfirmDialogOpen(true)}
>
{!isPending ? (
<span className="icon-[material-symbols--close-rounded] text-2xl" />
) : (
<span className="icon-[mdi--loading] animate-spin text-2xl text-icon-main" />
)}
</button>
</Tooltip>
</div>
</div>
</div>
<DeleteBookingModal
open={confirmDialogOpen}
onOpenChange={setConfirmDialogOpen}
onConfirm={() => {
deleteBooking();
setConfirmDialogOpen(false);
}}
/>
</>
);
}
97 changes: 97 additions & 0 deletions src/components/room-booking/list/DeleteBookingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
FloatingFocusManager,
FloatingOverlay,
FloatingPortal,
useDismiss,
useFloating,
useInteractions,
useRole,
useTransitionStyles,
} from "@floating-ui/react";
import { useRef } from "react";

export function DeleteBookingModal({
open,
onOpenChange,
onConfirm,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
onConfirm: () => void;
}) {
const { context, refs } = useFloating({ open, onOpenChange });

// Transition effect
const { isMounted, styles: transitionStyles } = useTransitionStyles(context);

// Event listeners to change the open state
const dismiss = useDismiss(context, { capture: true });
// Role props for screen readers
const role = useRole(context);

const { getFloatingProps } = useInteractions([dismiss, role]);

const cancelRef = useRef<HTMLButtonElement>(null);

if (!isMounted) {
return null;
}

return (
<FloatingPortal>
<FloatingOverlay
className="z-10 grid place-items-center bg-black/75 @container/modal"
lockScroll
>
<FloatingFocusManager context={context} initialFocus={cancelRef} modal>
<div
ref={refs.setFloating}
style={transitionStyles}
{...getFloatingProps()}
className="flex h-fit w-full max-w-lg flex-col p-4 outline-none"
>
<div className="overflow-hidden rounded-2xl bg-popup">
<div className="flex flex-col p-4 @2xl/modal:p-8">
{/* Heading and description */}
<div className="mb-4 flex w-full flex-row">
<div className="grow items-center text-3xl font-semibold">
Confirm deletion
</div>
<button
className="-mr-2 -mt-2 h-52 w-52 rounded-2xl p-2 text-icon-main/50 hover:bg-primary-hover/50 hover:text-icon-hover/75 @lg/export:-mr-6 @lg/export:-mt-6"
onClick={() => onOpenChange(false)}
>
<span className="icon-[material-symbols--close] text-4xl" />
</button>
</div>

<div className="flex flex-col gap-2">
<div className="flex flex-row gap-2 text-xl text-text-secondary/75">
<p className="flex w-full items-center whitespace-pre-wrap py-1 font-semibold [overflow-wrap:anywhere]">
Are you sure you want to delete this booking?
</p>
</div>

<div className="flex flex-row gap-2">
<button
className="flex w-full items-center justify-center gap-4 rounded-2xl bg-primary-main px-4 py-2 text-lg font-medium hover:bg-primary-hover dark:bg-primary-hover dark:hover:bg-primary-main"
onClick={() => onOpenChange(false)}
>
Back
</button>
<button
className="flex w-full items-center justify-center gap-2 rounded-2xl border-2 border-red-400 bg-red-200 px-4 py-2 text-lg font-medium text-red-900 hover:bg-red-300 dark:border-red-600 dark:bg-red-900 dark:text-red-300 dark:hover:bg-red-950"
onClick={() => onConfirm()}
>
Delete
</button>
</div>
</div>
</div>
</div>
</div>
</FloatingFocusManager>
</FloatingOverlay>
</FloatingPortal>
);
}

0 comments on commit 825ed43

Please sign in to comment.