Feat/team owner booking (#3999)
* WIP: testing queries * feat: add badge * fix: get only id * refactor: get bookings query * WIP: display attendees added * fix: add typepull/4402/head^2
parent
7e52e3d295
commit
1deca816bd
|
@ -327,8 +327,14 @@ function BookingListItem(booking: BookingItemProps) {
|
||||||
"max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max",
|
"max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max",
|
||||||
isCancelled ? "line-through" : ""
|
isCancelled ? "line-through" : ""
|
||||||
)}>
|
)}>
|
||||||
{booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>}
|
|
||||||
{booking.title}
|
{booking.title}
|
||||||
|
<span> </span>
|
||||||
|
{booking.eventType?.team && <Badge variant="gray">{booking.eventType.team.name}</Badge>}
|
||||||
|
|
||||||
|
{!!booking?.eventType?.price && !booking.paid && (
|
||||||
|
<Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">Pending payment</Tag>
|
||||||
|
)}
|
||||||
|
{isPending && <Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">{t("unconfirmed")}</Tag>}
|
||||||
</div>
|
</div>
|
||||||
{booking.description && (
|
{booking.description && (
|
||||||
<div
|
<div
|
||||||
|
@ -337,14 +343,12 @@ function BookingListItem(booking: BookingItemProps) {
|
||||||
"{booking.description}"
|
"{booking.description}"
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{booking.attendees.length !== 0 && (
|
{booking.attendees.length !== 0 && (
|
||||||
<a
|
<DisplayAttendees
|
||||||
className="text-sm text-gray-900 hover:text-blue-500"
|
attendees={booking.attendees}
|
||||||
href={"mailto:" + booking.attendees[0].email}
|
user={booking.user}
|
||||||
onClick={(e) => e.stopPropagation()}>
|
currentEmail={user?.email}
|
||||||
{booking.attendees[0].email}
|
/>
|
||||||
</a>
|
|
||||||
)}
|
)}
|
||||||
{isCancelled && booking.rescheduled && (
|
{isCancelled && booking.rescheduled && (
|
||||||
<div className="mt-2 inline-block text-left text-sm md:hidden">
|
<div className="mt-2 inline-block text-left text-sm md:hidden">
|
||||||
|
@ -373,4 +377,97 @@ function BookingListItem(booking: BookingItemProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserProps {
|
||||||
|
id: number;
|
||||||
|
name: string | null;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FirstAttendee = ({
|
||||||
|
user,
|
||||||
|
currentEmail,
|
||||||
|
}: {
|
||||||
|
user: UserProps;
|
||||||
|
currentEmail: string | null | undefined;
|
||||||
|
}) => {
|
||||||
|
return user.email === currentEmail ? (
|
||||||
|
<div className="inline-block">You</div>
|
||||||
|
) : (
|
||||||
|
<a
|
||||||
|
key={user.email}
|
||||||
|
className=" hover:text-blue-500"
|
||||||
|
href={"mailto:" + user.email}
|
||||||
|
onClick={(e) => e.stopPropagation()}>
|
||||||
|
{user.name}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Attendee: React.FC<{ email: string; children: React.ReactNode }> = ({ email, children }) => {
|
||||||
|
return (
|
||||||
|
<a className=" hover:text-blue-500" href={"mailto:" + email} onClick={(e) => e.stopPropagation()}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface AttendeeProps {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DisplayAttendees = ({
|
||||||
|
attendees,
|
||||||
|
user,
|
||||||
|
currentEmail,
|
||||||
|
}: {
|
||||||
|
attendees: AttendeeProps[];
|
||||||
|
user: UserProps | null;
|
||||||
|
currentEmail: string | null | undefined;
|
||||||
|
}) => {
|
||||||
|
if (attendees.length === 1) {
|
||||||
|
return (
|
||||||
|
<div className="text-sm text-gray-900">
|
||||||
|
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
|
||||||
|
<span> and </span>
|
||||||
|
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (attendees.length === 2) {
|
||||||
|
return (
|
||||||
|
<div className="text-sm text-gray-900">
|
||||||
|
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
|
||||||
|
<span>, </span>
|
||||||
|
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
|
||||||
|
<div className="inline-block text-sm text-gray-900"> and </div>
|
||||||
|
<Attendee email={attendees[1].email}>{attendees[1].name}</Attendee>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className="text-sm text-gray-900">
|
||||||
|
{user && <FirstAttendee user={user} currentEmail={currentEmail} />}
|
||||||
|
<span>, </span>
|
||||||
|
<Attendee email={attendees[0].email}>{attendees[0].name}</Attendee>
|
||||||
|
<span> & </span>
|
||||||
|
<Tooltip
|
||||||
|
content={attendees.slice(1).map((attendee, key) => (
|
||||||
|
<p key={key}>{attendee.name}</p>
|
||||||
|
))}>
|
||||||
|
<div className="inline-block">{attendees.length - 1} more</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ${className}`}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default BookingListItem;
|
export default BookingListItem;
|
||||||
|
|
|
@ -510,6 +510,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
||||||
};
|
};
|
||||||
const passedBookingsFilter = bookingListingFilters[bookingListingByStatus];
|
const passedBookingsFilter = bookingListingFilters[bookingListingByStatus];
|
||||||
const orderBy = bookingListingOrderby[bookingListingByStatus];
|
const orderBy = bookingListingOrderby[bookingListingByStatus];
|
||||||
|
|
||||||
const bookingsQuery = await prisma.booking.findMany({
|
const bookingsQuery = await prisma.booking.findMany({
|
||||||
where: {
|
where: {
|
||||||
OR: [
|
OR: [
|
||||||
|
@ -523,6 +524,18 @@ const loggedInViewerRouter = createProtectedRouter()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
eventType: {
|
||||||
|
team: {
|
||||||
|
members: {
|
||||||
|
some: {
|
||||||
|
userId: user.id,
|
||||||
|
role: "OWNER",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
AND: [passedBookingsFilter],
|
AND: [passedBookingsFilter],
|
||||||
},
|
},
|
||||||
|
@ -550,6 +563,8 @@ const loggedInViewerRouter = createProtectedRouter()
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
name: true,
|
||||||
|
email: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rescheduled: true,
|
rescheduled: true,
|
||||||
|
|
Loading…
Reference in New Issue