mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: enhance organization invitation UI and add organization details
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
Reference in New Issue
Block a user