For release.

This commit is contained in:
charlesgauthereau
2025-07-28 16:53:21 +02:00
parent 85047e7509
commit 17469696ee
13 changed files with 172 additions and 65 deletions
@@ -10,7 +10,7 @@ import {Trash2} from "lucide-react";
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/button-delete-account/delete-account.action";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import {User} from "@/db/schema/01_user";
import {useSession} from "@/lib/auth/auth-client";
import {authClient, useSession} from "@/lib/auth/auth-client";
import {formatFrenchDate} from "@/utils/date-formatting";
export const usersColumnsAdmin: ColumnDef<User>[] = [
@@ -19,7 +19,12 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
header: "Role",
cell: ({row}) => {
const [role, setRole] = useState<string>(row.getValue("role"));
const { data: session } = useSession();
const {data: session, isPending, error} = authClient.useSession();
const isCurrentUser = session?.user.email === row.original.email;
const updateMutation = useMutation({
@@ -38,6 +43,15 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
await updateMutation.mutateAsync();
};
if (isPending) return null;
if (error || !session) {
return null;
}
return (
<Badge
className={isCurrentUser ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
@@ -22,6 +22,7 @@ export const SidebarMenuCustomMain = () => {
return null;
}
const groupContent: SidebarGroupItem["group_content"] = [
{ title: "Dashboard", url: "/home", icon: Home },
{ title: "Projects", url: "/projects", icon: Layers, details:true },
@@ -1,11 +1,13 @@
"use server";
import { userAction } from "@/safe-actions";
import { OrganizationSchema } from "@/components/wrappers/dashboard/organization/organization.schema";
import { ServerActionResult } from "@/types/action-type";
import { z } from "zod";
import { OrganizationFormSchema } from "@/components/wrappers/dashboard/organization/organization-form/organization-form.schema";
import { db } from "@/db";
import {userAction} from "@/safe-actions";
import {OrganizationSchema} from "@/components/wrappers/dashboard/organization/organization.schema";
import {ServerActionResult} from "@/types/action-type";
import {z} from "zod";
import {
OrganizationFormSchema
} from "@/components/wrappers/dashboard/organization/organization-form/organization-form.schema";
import {db} from "@/db";
import {and, eq, inArray} from "drizzle-orm";
import {auth, checkSlugOrganization, createOrganization, deleteOrganization} from "@/lib/auth/auth";
import {slugify} from "@/utils/slugify";
@@ -13,7 +15,7 @@ import {Organization} from "@/db/schema/02_organization";
import * as drizzleDb from "@/db";
import {headers} from "next/headers";
export const createOrganizationAction = userAction.schema(OrganizationSchema).action(async ({ parsedInput }): Promise<ServerActionResult<Organization>> => {
export const createOrganizationAction = userAction.schema(OrganizationSchema).action(async ({parsedInput}): Promise<ServerActionResult<Organization>> => {
try {
const slug = slugify(parsedInput.name);
if (!await checkSlugOrganization(slug)) {
@@ -22,7 +24,7 @@ export const createOrganizationAction = userAction.schema(OrganizationSchema).ac
actionError: {
message: "Slug is already taken",
status: 500,
messageParams: { message: "Error creating the organization" },
messageParams: {message: "Error creating the organization"},
},
};
}
@@ -39,7 +41,7 @@ export const createOrganizationAction = userAction.schema(OrganizationSchema).ac
message: authError.message || "Authentication service error.",
status: authError.status || 500,
cause: "auth_error",
messageParams: { message: authError.message },
messageParams: {message: authError.message},
},
};
}
@@ -49,7 +51,7 @@ export const createOrganizationAction = userAction.schema(OrganizationSchema).ac
value: createdOrganization,
actionSuccess: {
message: "Organization has been successfully created.",
messageParams: { organizationId: createdOrganization!.id },
messageParams: {organizationId: createdOrganization!.id},
},
};
} catch (error) {
@@ -59,7 +61,7 @@ export const createOrganizationAction = userAction.schema(OrganizationSchema).ac
actionError: {
message: "Failed to create organization.",
status: 500,
messageParams: { message: "Error creating the organization" },
messageParams: {message: "Error creating the organization"},
},
};
}
@@ -72,7 +74,7 @@ export const updateOrganizationAction = userAction
organizationId: z.string(),
})
)
.action(async ({ parsedInput, ctx }): Promise<ServerActionResult<Organization>> => {
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => {
try {
const newUserList = parsedInput.data.users;
console.log(parsedInput);
@@ -127,7 +129,7 @@ export const updateOrganizationAction = userAction
}
if (usersToRemove.length > 0) {
await db.delete(drizzleDb.schemas.member).where(inArray(drizzleDb.schemas.member.userId, usersToRemove)).execute();
await db.delete(drizzleDb.schemas.member).where(and(inArray(drizzleDb.schemas.member.userId, usersToRemove), eq(drizzleDb.schemas.member.organizationId, organization.id))).execute();
// TODO : Do not delete, go permission error with better auth
// for (const userToRemove of usersToRemove) {
//
@@ -182,7 +184,7 @@ export const updateOrganizationAction = userAction
value: updatedOrganization as unknown as Organization,
actionSuccess: {
message: "Organization has been successfully updated.",
messageParams: { organizationId: organization.id},
messageParams: {organizationId: organization.id},
},
};
} catch (error) {
@@ -193,14 +195,14 @@ export const updateOrganizationAction = userAction
message: "Failed to update organization.",
status: 500,
cause: "server_error",
messageParams: { message: "Error updating the organization" },
messageParams: {message: "Error updating the organization"},
},
};
}
});
export const deleteOrganizationAction = userAction.schema(z.string()).action(
async ({ parsedInput, ctx }): Promise<ServerActionResult<Organization>> => {
async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => {
try {
const org = await db.query.organization.findFirst({
where: eq(drizzleDb.schemas.organization.slug, parsedInput),
@@ -229,7 +231,7 @@ export const deleteOrganizationAction = userAction.schema(z.string()).action(
message: authError.message || "Authentication service error.",
status: authError.status || 500,
cause: "auth_error",
messageParams: { message: authError.message },
messageParams: {message: authError.message},
},
};
}
@@ -239,7 +241,7 @@ export const deleteOrganizationAction = userAction.schema(z.string()).action(
value: deletedOrganization,
actionSuccess: {
message: "Organization has been successfully deleted.",
messageParams: { organizationId: deletedOrganization.id },
messageParams: {organizationId: deletedOrganization.id},
},
};
} catch (error) {
@@ -250,7 +252,7 @@ export const deleteOrganizationAction = userAction.schema(z.string()).action(
message: "Failed to delete organization due to a server error.",
status: 500,
cause: "server_error",
messageParams: { message: "Internal server error while deleting the organization" },
messageParams: {message: "Internal server error while deleting the organization"},
},
};
}
@@ -1,43 +1,58 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import {ColumnDef} from "@tanstack/react-table";
import {MemberWithUser} from "@/db/schema/02_organization";
import {useState} from "react";
import {useSession} from "@/lib/auth/auth-client";
import {authClient, useSession} from "@/lib/auth/auth-client";
import {useMutation} from "@tanstack/react-query";
import {updateUserAction} from "@/components/wrappers/dashboard/profile/user-form/user-form.action";
import {toast} from "sonner";
import {Badge} from "@/components/ui/badge";
import {updateMemberRoleAction} from "@/components/wrappers/dashboard/settings/update-member.action";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/settings/member.schema";
export const organizationMemberColumns: ColumnDef<MemberWithUser>[] = [
{
accessorKey: "role",
header: "Role",
cell: ({row}) => {
cell: ({ row }) => {
const [role, setRole] = useState<string>(row.getValue("role"));
const { data: session } = useSession();
const isCurrentUser = session?.user.email === row.original.user.email;
// const updateMutation = useMutation({
// mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
// onSuccess: () => {
// toast.success(`User updated successfully.`);
// },
// onError: () => {
// toast.error(`An error occurred while updating user information.`);
// },
// });
const updateMutation = useMutation({
mutationFn: () =>
updateMemberRoleAction({
memberId: row.original.id,
organizationId: row.original.organizationId,
role: RoleSchemaMember.parse(role),
}),
onSuccess: () => {
toast.success(`User updated successfully.`);
},
onError: () => {
toast.error(`An error occurred while updating user information.`);
},
});
const handleUpdateRole = async () => {
// const nextRole = role === "admin" ? "pending" : role === "pending" ? "user" : "admin";
// setRole(nextRole);
// await updateMutation.mutateAsync();
const nextRole =
role === "owner" ? "admin" : role === "admin" ? "member" : "owner";
setRole(nextRole);
await updateMutation.mutateAsync();
};
const isCurrentUser = session?.user.email === row.original.user.email;
const isMember = session?.user.role === "member";
const isDisabled = isMember || isCurrentUser;
return (
<Badge
className={isCurrentUser ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
onClick={isCurrentUser ? undefined : () => handleUpdateRole()}
className={isDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"}
onClick={isDisabled ? undefined : handleUpdateRole}
variant="outline"
>
{role}
@@ -0,0 +1,8 @@
import {z} from "zod";
const RoleEnum = z.enum(["member", "owner", "admin"]);
export const RoleSchemaMember = z.union([
RoleEnum,
z.array(RoleEnum)
]);
@@ -0,0 +1,51 @@
"use server";
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {auth} from "@/lib/auth/auth";
import {ServerActionResult} from "@/types/action-type";
import {Member} from "better-auth/plugins";
import {RoleSchemaMember} from "@/components/wrappers/dashboard/settings/member.schema";
import {headers} from "next/headers";
export const updateMemberRoleAction = userAction.schema(
z.object({
memberId: z.string(),
organizationId: z.string(),
role: RoleSchemaMember,
})
).action(async ({parsedInput}): Promise<ServerActionResult<Member>> => {
try {
console.log(parsedInput);
const updatedMember = await auth.api.updateMemberRole({
body: {
role: parsedInput.role,
memberId: parsedInput.memberId,
organizationId: parsedInput.organizationId,
},
headers: await headers(),
});
console.log(updatedMember);
return {
success: true,
value: updatedMember,
actionSuccess: {
message: "Member has been successfully updated.",
messageParams: {},
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to update member role.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: {},
},
};
}
});