feat: enhance organization invitation UI and add organization details

This commit is contained in:
Mauricio Siu
2025-02-22 13:53:57 -06:00
parent c52725420e
commit 81a881b07e
3 changed files with 63 additions and 50 deletions

View File

@@ -555,7 +555,7 @@ function SidebarLogo() {
</SidebarMenuButton> </SidebarMenuButton>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent <DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg" className="w-[--radix-dropdown-menu-trigger-width] min-w-64 rounded-lg"
align="start" align="start"
side={isMobile ? "bottom" : "right"} side={isMobile ? "bottom" : "right"}
sideOffset={4} sideOffset={4}
@@ -634,9 +634,9 @@ function SidebarLogo() {
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="relative"> <Button variant="ghost" size="icon" className="relative">
<Bell className="h-5 w-5" /> <Bell className="size-4" />
{invitations && invitations.length > 0 && ( {invitations && invitations.length > 0 && (
<span className="absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 text-xs text-white"> <span className="absolute -top-0 -right-0 flex size-4 items-center justify-center rounded-full bg-blue-500 text-xs text-white">
{invitations.length} {invitations.length}
</span> </span>
)} )}
@@ -648,53 +648,58 @@ function SidebarLogo() {
className="w-80" className="w-80"
> >
<DropdownMenuLabel>Pending Invitations</DropdownMenuLabel> <DropdownMenuLabel>Pending Invitations</DropdownMenuLabel>
{invitations && invitations.length > 0 ? ( <div className="flex flex-col gap-2">
invitations.map((invitation) => ( {invitations && invitations.length > 0 ? (
<div key={invitation.id} className="flex flex-col gap-2"> invitations.map((invitation) => (
<DropdownMenuItem <div key={invitation.id} className="flex flex-col gap-2">
className="flex flex-col items-start gap-1 p-3" <DropdownMenuItem
onSelect={(e) => e.preventDefault()} className="flex flex-col items-start gap-1 p-3"
> onSelect={(e) => e.preventDefault()}
<div className="font-medium">{invitation.email}</div> >
<div className="text-xs text-muted-foreground"> <div className="font-medium">
Expires:{" "} {invitation?.organization?.name}
{new Date(invitation.expiresAt).toLocaleDateString()} </div>
</div> <div className="text-xs text-muted-foreground">
<div className="text-xs text-muted-foreground"> Expires:{" "}
Role: {invitation.role} {new Date(invitation.expiresAt).toLocaleString()}
</div> </div>
</DropdownMenuItem> <div className="text-xs text-muted-foreground">
<DialogAction Role: {invitation.role}
title="Accept Invitation" </div>
description="Are you sure you want to accept this invitation?" </DropdownMenuItem>
type="default" <DialogAction
onClick={async () => { title="Accept Invitation"
const { error } = description="Are you sure you want to accept this invitation?"
await authClient.organization.acceptInvitation({ type="default"
invitationId: invitation.id, onClick={async () => {
}); const { error } =
await authClient.organization.acceptInvitation({
invitationId: invitation.id,
});
if (error) { if (error) {
toast.error( toast.error(
error.message || "Error accepting invitation", error.message || "Error accepting invitation",
); );
} else { } else {
toast.success("Invitation accepted successfully"); toast.success("Invitation accepted successfully");
await refetchInvitations(); await refetchInvitations();
} await refetch();
}} }
> }}
<Button size="sm" variant="secondary"> >
Accept Invitation <Button size="sm" variant="secondary">
</Button> Accept Invitation
</DialogAction> </Button>
</div> </DialogAction>
)) </div>
) : ( ))
<DropdownMenuItem disabled> ) : (
No pending invitations <DropdownMenuItem disabled>
</DropdownMenuItem> No pending invitations
)} </DropdownMenuItem>
)}
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</SidebarMenuItem> </SidebarMenuItem>

View File

@@ -144,6 +144,9 @@ export const userRouter = createTRPCRouter({
gt(invitation.expiresAt, new Date()), gt(invitation.expiresAt, new Date()),
eq(invitation.status, "pending"), eq(invitation.status, "pending"),
), ),
with: {
organization: true,
},
}); });
}), }),
}); });

View File

@@ -140,7 +140,12 @@ export const apiRemoveUser = createSchema
}) })
.required(); .required();
export const apiFindOneToken = createSchema.pick({}).required(); export const apiFindOneToken = createSchema
.pick({})
.required()
.extend({
token: z.string().min(1),
});
export const apiAssignPermissions = createSchema export const apiAssignPermissions = createSchema
.pick({ .pick({