diff --git a/.gitignore b/.gitignore index 5e6e4eb3..21539388 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,6 @@ yarn-error.log* .DS_Store *.pem +.db -.db \ No newline at end of file +.tool-versions \ No newline at end of file diff --git a/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx b/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx index d05409fb..bfb05461 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx @@ -139,6 +139,7 @@ export const AddInvitation = () => { Member + Admin diff --git a/apps/dokploy/components/dashboard/settings/users/edit-role.tsx b/apps/dokploy/components/dashboard/settings/users/edit-role.tsx new file mode 100644 index 00000000..db409723 --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/users/edit-role.tsx @@ -0,0 +1,153 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Edit3Icon } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; + +const editUserRole = z.object({ + role: z.enum(["member", "admin"]), +}); + +type EditUserRole = z.infer; + +interface EditUserRoleProps { + userId: string; + currentRole: string; + userEmail: string; +} + +export const EditUserRole = ({ userId, currentRole, userEmail }: EditUserRoleProps) => { + const [open, setOpen] = useState(false); + const utils = api.useUtils(); + + const { mutateAsync: updateRoleMember, isLoading } = api.user.updateRoleMember.useMutation(); + + const form = useForm({ + defaultValues: { + role: currentRole, + } as EditUserRole, + resolver: zodResolver(editUserRole), + }); + + const onSubmit = async (data: EditUserRole) => { + if (data.role === currentRole) { + toast.info("No changes made"); + setOpen(false); + return; + } + + try { + await updateRoleMember({ + userId: userId, + role: data.role, + }); + + toast.success(`User role updated to ${data.role}`); + setOpen(false); + + utils.user.all.invalidate(); + } catch (error: any) { + toast.error(error?.message || "Error updating user role"); + } + }; + + return ( + + + e.preventDefault()} + > + + Edit Role + + + + + Edit User Role + + Change the role for {userEmail} + + + +
+ + ( + + Role + + + + )} + /> + + + + + + + +
+
+ ); +}; \ No newline at end of file diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 9580240d..01ce1a9a 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -31,6 +31,7 @@ import { MoreHorizontal, Users } from "lucide-react"; import { Loader2 } from "lucide-react"; import { toast } from "sonner"; import { AddUserPermissions } from "./add-permissions"; +import { EditUserRole } from "./edit-role"; export const ShowUsers = () => { const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -127,6 +128,14 @@ export const ShowUsers = () => { Actions + {member.role !== "owner" && ( + + )} + {member.role !== "owner" && ( { + const targetMember = await db.query.member.findFirst({ + where: and( + eq(member.userId, input.userId), + eq(member.organizationId, ctx.session.activeOrganizationId), + ), + }); + + if (!targetMember) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Member not found in this organization", + }); + } + + if (ctx.user.id === input.userId) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Cannot update your own role", + }); + } + + await db + .update(member) + .set({ + role: input.role, + }) + .where( + and( + eq(member.userId, input.userId), + eq(member.organizationId, ctx.session.activeOrganizationId), + ), + ); + + const updatedMember = await db.query.member.findFirst({ + where: and( + eq(member.userId, input.userId), + eq(member.organizationId, ctx.session.activeOrganizationId), + ), + with: { + user: true, + }, + }); + + return updatedMember; + + }), });