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",
|
||||
isCancelled ? "line-through" : ""
|
||||
)}>
|
||||
{booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>}
|
||||
{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>
|
||||
{booking.description && (
|
||||
<div
|
||||
|
@ -337,14 +343,12 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
"{booking.description}"
|
||||
</div>
|
||||
)}
|
||||
|
||||
{booking.attendees.length !== 0 && (
|
||||
<a
|
||||
className="text-sm text-gray-900 hover:text-blue-500"
|
||||
href={"mailto:" + booking.attendees[0].email}
|
||||
onClick={(e) => e.stopPropagation()}>
|
||||
{booking.attendees[0].email}
|
||||
</a>
|
||||
<DisplayAttendees
|
||||
attendees={booking.attendees}
|
||||
user={booking.user}
|
||||
currentEmail={user?.email}
|
||||
/>
|
||||
)}
|
||||
{isCancelled && booking.rescheduled && (
|
||||
<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;
|
||||
|
|
|
@ -510,6 +510,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
};
|
||||
const passedBookingsFilter = bookingListingFilters[bookingListingByStatus];
|
||||
const orderBy = bookingListingOrderby[bookingListingByStatus];
|
||||
|
||||
const bookingsQuery = await prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
|
@ -523,6 +524,18 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
eventType: {
|
||||
team: {
|
||||
members: {
|
||||
some: {
|
||||
userId: user.id,
|
||||
role: "OWNER",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsFilter],
|
||||
},
|
||||
|
@ -550,6 +563,8 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
rescheduled: true,
|
||||
|
|
Loading…
Reference in New Issue