Skip to content

Commit

Permalink
Merge pull request #25 from Ayon95/booking_delete
Browse files Browse the repository at this point in the history
Implement booking delete functionality
  • Loading branch information
Ayon95 authored Apr 23, 2024
2 parents af3a71a + 8f37015 commit b1ed214
Show file tree
Hide file tree
Showing 10 changed files with 381 additions and 105 deletions.
193 changes: 112 additions & 81 deletions src/features/bookings/BookingDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import BookingPaymentSummary from './BookingPaymentSummary';
import { Button } from '@/ui/button/Button';
import { useCheckOutBooking } from './hooks/useCheckOutBooking';
import SpinnerMini from '@/ui/spinner/SpinnerMini';
import useModal from '@/hooks/useModal';
import ConfirmDeleteModal from '@/ui/Modal/ConfirmDeleteModal';
import { useDeleteBooking } from './hooks/useDeleteBooking';

interface BookingProps {
booking: Booking;
Expand All @@ -25,92 +28,114 @@ const dateFormatOptions: Intl.DateTimeFormatOptions = {
};

function BookingDetails({ booking }: BookingProps) {
const {
shouldShowModal: shouldShowConfirmDeleteModal,
openModal: openConfirmDeleteModal,
closeModal: closeConfirmDeleteModal,
} = useModal();

const deleteBookingMutation = useDeleteBooking();
const checkOutBookingMutation = useCheckOutBooking();

return (
<Container>
<Header>
<HeadingContainer>
<Heading as="h1">Booking #{booking.id}</Heading>
<StyledBadge type={bookingStatusToBadgeColorMap[booking.status as BookingStatus]}>
{booking.status}
</StyledBadge>
</HeadingContainer>
<LinkButtonIconText to="/bookings">
<HiArrowLeft />
<span>Bookings</span>
</LinkButtonIconText>
</Header>
<BookingDate className="text-sm">
Booked on{' '}
<time dateTime={booking.created_at}>
{formatDate(booking.created_at, dateFormatOptions)}
</time>
</BookingDate>
<Grid>
<div>
<BookingDurationDetails>
<div>
<HiOutlineHomeModern />
<p>
<span className="fw-semi-bold">{booking.num_nights}</span>{' '}
{booking.num_nights > 1 ? 'nights' : 'night'} in{' '}
<span className="fw-semi-bold">Cabin {booking.cabin?.name}</span>
</p>
</div>
<div>
<p>Check-in</p>
<time dateTime={booking.start_date} className="fw-semi-bold">
{formatDate(booking.start_date, dateFormatOptions)}
</time>
</div>
<div>
<p>Check-out</p>
<time dateTime={booking.end_date} className="fw-semi-bold">
{formatDate(booking.end_date, dateFormatOptions)}
</time>
</div>
</BookingDurationDetails>
{booking.guest && (
<Section>
<Heading as="h2">Guest Details</Heading>
<BookingGuestDetailsTable
guestDetails={{ num_guests: booking.num_guests, guest: booking.guest }}
/>
</Section>
)}
{booking.observations && (
<Observations>
<Heading as="h2">Observations & Requests</Heading>
<p>{booking.observations}</p>
</Observations>
<>
<Container>
<Header>
<HeadingContainer>
<Heading as="h1">Booking #{booking.id}</Heading>
<StyledBadge type={bookingStatusToBadgeColorMap[booking.status as BookingStatus]}>
{booking.status}
</StyledBadge>
</HeadingContainer>
<LinkButtonIconText to="/bookings">
<HiArrowLeft />
<span>Bookings</span>
</LinkButtonIconText>
</Header>
<BookingDate className="text-sm">
Booked on{' '}
<time dateTime={booking.created_at}>
{formatDate(booking.created_at, dateFormatOptions)}
</time>
</BookingDate>
<Grid>
<div>
<BookingDurationDetails>
<div>
<HiOutlineHomeModern />
<p>
<span className="fw-semi-bold">{booking.num_nights}</span>{' '}
{booking.num_nights > 1 ? 'nights' : 'night'} in{' '}
<span className="fw-semi-bold">Cabin {booking.cabin?.name}</span>
</p>
</div>
<div>
<p>Check-in</p>
<time dateTime={booking.start_date} className="fw-semi-bold">
{formatDate(booking.start_date, dateFormatOptions)}
</time>
</div>
<div>
<p>Check-out</p>
<time dateTime={booking.end_date} className="fw-semi-bold">
{formatDate(booking.end_date, dateFormatOptions)}
</time>
</div>
</BookingDurationDetails>
{booking.guest && (
<Section>
<Heading as="h2">Guest Details</Heading>
<BookingGuestDetailsTable
guestDetails={{ num_guests: booking.num_guests, guest: booking.guest }}
/>
</Section>
)}
{booking.observations && (
<Observations>
<Heading as="h2">Observations & Requests</Heading>
<p>{booking.observations}</p>
</Observations>
)}
</div>

<BookingPaymentSummary
paymentInfo={{
cabin_price: booking.cabin_price,
extra_price: booking.extra_price,
total_price: booking.total_price,
has_breakfast: booking.has_breakfast,
is_paid: booking.is_paid,
}}
/>
</Grid>
<ButtonsContainer>
{booking.status === 'checked-in' && (
<Button
onClick={() =>
checkOutBookingMutation.mutate({
bookingId: booking.id,
updatedData: { status: 'checked-out' },
})
}
disabled={checkOutBookingMutation.isLoading}
>
{checkOutBookingMutation.isLoading ? <SpinnerMini /> : 'Check Out'}
</Button>
)}
</div>

<BookingPaymentSummary
paymentInfo={{
cabin_price: booking.cabin_price,
extra_price: booking.extra_price,
total_price: booking.total_price,
has_breakfast: booking.has_breakfast,
is_paid: booking.is_paid,
}}
<Button $variant="danger" onClick={openConfirmDeleteModal}>
Delete
</Button>
</ButtonsContainer>
</Container>
{shouldShowConfirmDeleteModal && (
<ConfirmDeleteModal
resourceName={`booking ${booking.id}`}
onConfirmDelete={() => deleteBookingMutation.mutate(booking.id)}
onCloseModal={closeConfirmDeleteModal}
isDeleting={deleteBookingMutation.isLoading}
/>
</Grid>
{booking.status === 'checked-in' && (
<Button
className="mt-3"
onClick={() =>
checkOutBookingMutation.mutate({
bookingId: booking.id,
updatedData: { status: 'checked-out' },
})
}
disabled={checkOutBookingMutation.isLoading}
>
{checkOutBookingMutation.isLoading ? <SpinnerMini /> : 'Check Out'}
</Button>
)}
</Container>
</>
);
}

Expand Down Expand Up @@ -182,3 +207,9 @@ const Observations = styled(Section)`
max-width: 75ch;
}
`;

const ButtonsContainer = styled.div`
margin-top: 3rem;
display: flex;
gap: 10px;
`;
18 changes: 15 additions & 3 deletions src/features/bookings/BookingRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled from 'styled-components';
import { HiArrowUpOnSquare, HiEye } from 'react-icons/hi2';
import { HiArrowUpOnSquare, HiEye, HiOutlineTrash } from 'react-icons/hi2';

import { Booking, BookingStatus } from '@/types/bookings';
import Table from '@/ui/Table';
Expand All @@ -11,9 +11,10 @@ import { useCheckOutBooking } from './hooks/useCheckOutBooking';

interface BookingRowProps {
booking: Booking;
onClickDelete: (booking: Booking) => void;
}

function BookingRow({ booking }: BookingRowProps) {
function BookingRow({ booking, onClickDelete }: BookingRowProps) {
const { id, cabin, guest, start_date, end_date, num_nights, total_price, status } = booking;
const checkOutBookingMutation = useCheckOutBooking();

Expand Down Expand Up @@ -56,6 +57,15 @@ function BookingRow({ booking }: BookingRowProps) {
<span>Check out</span>
</ButtonIconText>
)}
<ButtonIconText
type="button"
aria-haspopup="dialog"
$variant="danger"
onClick={() => onClickDelete(booking)}
>
<HiOutlineTrash />
<span>Delete</span>
</ButtonIconText>
</ActionButtonsContainer>
</Table.Cell>
</Table.Row>
Expand All @@ -71,5 +81,7 @@ const DarkNumericTextCell = styled(Table.Cell)`

const ActionButtonsContainer = styled.div`
display: flex;
gap: 12px;
flex-direction: column;
flex-wrap: wrap;
gap: 8px;
`;
84 changes: 65 additions & 19 deletions src/features/bookings/BookingTable.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,78 @@
import { useState } from 'react';

import { Booking } from '@/types/bookings';
import Table from '@/ui/Table';
import BookingRow from './BookingRow';
import useModal from '@/hooks/useModal';
import { useDeleteBooking } from './hooks/useDeleteBooking';
import ConfirmDeleteModal from '@/ui/Modal/ConfirmDeleteModal';

interface BookingTableProps {
bookings: Booking[];
}

function BookingTable({ bookings }: BookingTableProps) {
const [selectedBooking, setSelectedBooking] = useState<null | Booking>(null);

const deleteBookingMutation = useDeleteBooking();

const {
shouldShowModal: shouldShowConfirmDeleteModal,
openModal: openConfirmDeleteModal,
closeModal: closeConfirmDeleteModal,
} = useModal();

function showConfirmDeleteModalForSelectedBooking(booking: Booking) {
setSelectedBooking(booking);
openConfirmDeleteModal();
}

function closeConfirmDeleteModalForSelectedBooking() {
setSelectedBooking(null);
closeConfirmDeleteModal();
}

function deleteSelectedBooking() {
if (selectedBooking) {
deleteBookingMutation.mutate(selectedBooking.id);
closeConfirmDeleteModalForSelectedBooking();
}
}

return (
<Table id="bookingTable" caption="Bookings">
<Table.Head>
<Table.Row>
<Table.HeaderCell>Cabin</Table.HeaderCell>
<Table.HeaderCell>Guest</Table.HeaderCell>
<Table.HeaderCell>Duration</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Price</Table.HeaderCell>
<Table.HeaderCell>
<span className="sr-only">Actions</span>
</Table.HeaderCell>
</Table.Row>
</Table.Head>
<Table.Body>
{bookings.map(booking => (
<BookingRow key={booking.id} booking={booking} />
))}
</Table.Body>
</Table>
<>
<Table id="bookingTable" caption="Bookings">
<Table.Head>
<Table.Row>
<Table.HeaderCell>Cabin</Table.HeaderCell>
<Table.HeaderCell>Guest</Table.HeaderCell>
<Table.HeaderCell>Duration</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Price</Table.HeaderCell>
<Table.HeaderCell>
<span className="sr-only">Actions</span>
</Table.HeaderCell>
</Table.Row>
</Table.Head>
<Table.Body>
{bookings.map(booking => (
<BookingRow
key={booking.id}
booking={booking}
onClickDelete={showConfirmDeleteModalForSelectedBooking}
/>
))}
</Table.Body>
</Table>
{selectedBooking && shouldShowConfirmDeleteModal && (
<ConfirmDeleteModal
resourceName={`booking ${selectedBooking.id}`}
onConfirmDelete={deleteSelectedBooking}
onCloseModal={closeConfirmDeleteModalForSelectedBooking}
isDeleting={deleteBookingMutation.isLoading}
/>
)}
</>
);
}

Expand Down
Loading

0 comments on commit b1ed214

Please sign in to comment.