Skip to content

Commit

Permalink
Merge branch 'main' into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
lucia-gomez committed Apr 11, 2024
2 parents d683304 + 62cc57c commit a200127
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export default function Admin() {
{tab === 'adminUsers' && <AdminUsers />}
{tab === 'paUsers' && <PAUsers />}
{tab === 'liaesons' && <Liaisons />}
{tab === 'bookings' && <Bookings showNnumber={true} />}
{tab === 'bookings' && <Bookings isAdminView={true} />}
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ import { useLocation } from 'react-router';

interface Props {
calendarEventId: string;
setOptimisticStatus: (x: BookingStatusLabel) => void;
status: BookingStatusLabel;
}

export default function BookingActions({ status, calendarEventId }: Props) {
const [loading, setLoading] = useState(false);
export default function BookingActions({
status,
calendarEventId,
setOptimisticStatus,
}: Props) {
const [uiLoading, setUiLoading] = useState(false);
const { reloadBookings, reloadBookingStatuses } = useContext(DatabaseContext);

const location = useLocation();
Expand All @@ -22,67 +27,112 @@ export default function BookingActions({ status, calendarEventId }: Props) {
await Promise.all([reloadBookings(), reloadBookingStatuses()]);
};

const ActionButton = (text: string, action: () => Promise<void>) => (
const onError = () => alert();

const ActionButton = (
text: string,
action: () => Promise<void>,
optimisticNextStatus: BookingStatusLabel
) => (
<button
className="font-medium text-blue-600 dark:text-blue-500 hover:underline mr-2"
onClick={async () => {
setLoading(true);
setUiLoading(true);
setOptimisticStatus(optimisticNextStatus);
try {
await action();
await reload();
action()
.catch(() => {
onError();
setOptimisticStatus(undefined);
})
.finally(reload);
} catch (ex) {
console.error(ex);
alert('Failed to perform action on booking');
} finally {
setLoading(false);
setUiLoading(false);
}
}}
>
{text}
</button>
);

if (loading) {
if (uiLoading) {
return (
<td className="px-2 py-4 w-28">
<Loading />
</td>
);
}

const paBtns = (
<>
{status !== BookingStatusLabel.CHECKED_IN &&
ActionButton('Check In', () =>
serverFunctions.checkin(calendarEventId)
)}
{status !== BookingStatusLabel.NO_SHOW &&
ActionButton('No Show', () => serverFunctions.noShow(calendarEventId))}
</>
);
const paBtns = () => {
const checkInBtn = ActionButton(
'Check In',
() => serverFunctions.checkin(calendarEventId),
BookingStatusLabel.CHECKED_IN
);
const noShowBtn = ActionButton(
'No Show',
() => serverFunctions.noShow(calendarEventId),
BookingStatusLabel.NO_SHOW
);

if (status === BookingStatusLabel.APPROVED) {
return (
<>
{checkInBtn}
{noShowBtn}
</>
);
} else if (status === BookingStatusLabel.CHECKED_IN) {
return noShowBtn;
} else if (status === BookingStatusLabel.NO_SHOW) {
return checkInBtn;
}
};

if (!isAdminPage) {
return (
<td className="px-2 py-4 w-28">
<div className="flex flex-col items-start">{paBtns}</div>
<div className="flex flex-col items-start">{paBtns()}</div>
</td>
);
}

if (
status === BookingStatusLabel.CANCELED ||
status === BookingStatusLabel.REJECTED
) {
return <p></p>;
}

return (
<td className="px-2 py-4 w-36">
<td className="px-2 py-4 w-40">
<div className="flex flex-col items-start">
{status === BookingStatusLabel.PRE_APPROVED &&
ActionButton('2nd Approve', () =>
serverFunctions.approveBooking(calendarEventId)
ActionButton(
'2nd Approve',
() => serverFunctions.approveBooking(calendarEventId),
BookingStatusLabel.APPROVED
)}
{status === BookingStatusLabel.REQUESTED &&
ActionButton('1st Approve', () =>
serverFunctions.approveBooking(calendarEventId)
ActionButton(
'1st Approve',
() => serverFunctions.approveBooking(calendarEventId),
BookingStatusLabel.PRE_APPROVED
)}
{ActionButton('Reject', () => serverFunctions.reject(calendarEventId))}
{ActionButton('Cancel', () => serverFunctions.cancel(calendarEventId))}
{paBtns}
{ActionButton(
'Reject',
() => serverFunctions.reject(calendarEventId),
BookingStatusLabel.REJECTED
)}
{ActionButton(
'Cancel',
() => serverFunctions.cancel(calendarEventId),
BookingStatusLabel.CANCELED
)}
{paBtns()}
</div>
</td>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Booking, BookingStatusLabel } from '../../../../types';
import React, { useContext, useState } from 'react';

import BookingActions from './BookingActions';
import { DatabaseContext } from '../../components/Provider';
import { formatDate } from '../../../utils/date';
import getBookingStatus from '../hooks/getBookingStatus';

interface Props {
booking: Booking;
isAdminView: boolean;
isUserView: boolean;
}

export default function BookingTableRow({
booking,
isAdminView,
isUserView,
}: Props) {
const { bookingStatuses } = useContext(DatabaseContext);
const status = getBookingStatus(booking, bookingStatuses);

const [optimisticStatus, setOptimisticStatus] =
useState<BookingStatusLabel>();

return (
<tr className="">
{!isUserView && (
<BookingActions
status={optimisticStatus ?? status}
calendarEventId={booking.calendarEventId}
{...{ setOptimisticStatus }}
/>
)}
<td className="px-2 py-4 w-28">{optimisticStatus ?? status}</td>
<td className="px-2 py-4 w-36">{booking.roomId}</td>
<td scope="row" className="px-2 py-4 w-40 text-gray-900 dark:text-white">
<div className="pl-3 w-full">
<div className="flex flex-col">
<div className="text-base font-semibold">
{booking.firstName} {booking.lastName}
</div>
<div className="font-normal text-gray-500">{booking.email}</div>
<div className="font-normal text-gray-500">
{booking.phoneNumber}
</div>
</div>
</div>
</td>
<td className="px-2 py-4 w-40">
<div className=" flex items-center flex-col">
<div>{formatDate(booking.startDate)}</div>
</div>
</td>
<td className="px-2 py-4 w-40">
<div className=" flex items-center flex-col">
<div>{formatDate(booking.endDate)}</div>
</div>
</td>
<td className="px-2 py-4 w-36">{booking.secondaryName}</td>
{isAdminView && <td className="px-2 py-4 w-20">{booking.nNumber}</td>}
<td className="px-2 py-4 w-20">{booking.netId}</td>
<td className="px-2 py-4 w-36">{booking.department}</td>
<td className="px-2 py-4 w-20">{booking.role}</td>
<td className="px-2 py-4 w-24">
{booking.sponsorFirstName} {booking.sponsorLastName}
</td>
<td className="px-2 py-4 w-20">{booking.sponsorEmail}</td>
<td className="px-2 py-4 w-52 break-all">{booking.title}</td>
<td className="px-2 py-4 w-60 break-all">{booking.description}</td>
<td className="px-2 py-4 w-20">{booking.expectedAttendance}</td>
<td className="px-2 py-4 w-20">{booking.attendeeAffiliation}</td>
<td className="px-2 py-4 w-40">
{booking.roomSetup}
{booking.setupDetails && (
<>
<br />
<b>Details</b>
<br />
{booking.setupDetails}
</>
)}
</td>
<td className="px-2 py-4 w-24">{booking.chartFieldForRoomSetup}</td>
<td className="px-2 py-4 w-40">
{booking.mediaServices}
{booking.mediaServicesDetails && (
<>
<br />
<b>Details</b>
<br />
{booking.mediaServicesDetails}
</>
)}
</td>
<td className="px-2 py-4 w-18">{booking.catering}</td>
<td className="px-2 py-4 w-18">{booking.cateringService}</td>
<td className="px-2 py-4 w-24">{booking.chartFieldForCatering}</td>
<td className="px-2 py-4 w-18">{booking.hireSecurity}</td>
<td className="px-2 py-4 w-24">{booking.chartFieldForSecurity}</td>
</tr>
);
}
Loading

0 comments on commit a200127

Please sign in to comment.