Working on some bugs.

This commit is contained in:
charlesgauthereau
2025-07-27 21:26:31 +02:00
parent f9dc15ba73
commit 85047e7509
107 changed files with 971 additions and 2203 deletions
@@ -4,6 +4,8 @@ import {ComboBox} from "@/components/wrappers/common/combobox";
import {useSidebar} from "@/components/ui/sidebar";
import {useRouter} from "next/navigation";
import {authClient} from "@/lib/auth/auth-client";
import {auth} from "@/lib/auth/auth";
export function OrganizationCombobox() {
const router = useRouter();
@@ -24,11 +26,15 @@ export function OrganizationCombobox() {
};
});
const onValueChange = async (slug: string) => {
await authClient.organization.setActive({
organizationSlug: slug,
});
router.refresh();
};
const handleReset = () => {
@@ -7,7 +7,7 @@ export const OrganizationFormSchema = z.object({
.regex(/^[a-zA-Z0-9_-]*$/, 'Slug can only contain letters, numbers, underscores, and hyphens')
.min(5, 'Slug must be at least 5 characters long')
.max(20, 'Slug must be at most 20 characters long'),
members: z.array(z.string()),
users: z.array(z.string()),
});
@@ -1,19 +1,38 @@
"use client";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, useZodForm } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Form } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { useMutation } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { MultiSelect } from "@/components/wrappers/common/multiselect/multi-select";
import { OrganizationFormSchema, OrganizationFormType } from "@/components/wrappers/dashboard/organization/OrganizationForm/organization-form.schema";
import {Card, CardContent, CardHeader} from "@/components/ui/card";
import {
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
useZodForm
} from "@/components/ui/form";
import {Input} from "@/components/ui/input";
import {Form} from "@/components/ui/form";
import {Button} from "@/components/ui/button";
import {useMutation} from "@tanstack/react-query";
import {useRouter} from "next/navigation";
import {MultiSelect} from "@/components/wrappers/common/multiselect/multi-select";
import {
OrganizationFormSchema,
OrganizationFormType
} from "@/components/wrappers/dashboard/organization/organization-form/organization-form.schema";
import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/02_organization";
import {
deleteOrganizationAction,
updateOrganizationAction
} from "@/components/wrappers/dashboard/organization/organization.action";
import {toast} from "sonner";
import {User as BetterAuthUser} from "better-auth";
import {User} from "@/db/schema/01_user";
export type organizationFormProps = {
defaultValues?: OrganizationWithMembers;
members: MemberWithUser[];
users: User[];
currentUser: BetterAuthUser;
};
export const OrganizationForm = (props: organizationFormProps) => {
@@ -21,17 +40,20 @@ export const OrganizationForm = (props: organizationFormProps) => {
const router = useRouter();
const isCreate = !Boolean(props.defaultValues);
const formatUsersList = (members: MemberWithUser[]) => {
const formatUsersList = (users: User[]) => {
return members.map((member) => ({
value: member.user.id,
label: `${member.user.name} | ${member.user.email}`,
}));
return users
.filter((user) => user.id !== props.currentUser.id)
.map((user) => ({
value: user.id,
label: `${user.name} | ${user.email}`,
}));
};
const formatDefaultUsers = (members: MemberWithUser[]): string[] => {
console.log("ici",members);
return members.map((member) => member.userId);
return members
.filter((member) => member.userId !== props.currentUser.id)
.map((member) => member.userId);
};
const formattedDefaultValues = {
@@ -46,21 +68,28 @@ export const OrganizationForm = (props: organizationFormProps) => {
});
const mutation = useMutation({
mutationFn: async (values: OrganizationFormType) => {
console.log(values);
// const organization = await updateOrganizationAction({ data: values, organizationId: props.defaultValues.id });
// console.log(organization);
// if (organization.data.success) {
// // toast.success(organization.data.actionSuccess.message);
// // router.push(`/dashboard/${organization.data.value.slug}/settings`);
// // router.refresh();
// } else {
// // toast.success(organization.data.actionError.message);
// }
mutationFn: (values: OrganizationFormType) => updateOrganizationAction({
data: values,
organizationId: props.defaultValues?.id ?? ""
}),
onSuccess: async (result) => {
if (result?.data?.success) {
toast.success(result.data.actionSuccess?.message || "Organization updated successfully.");
router.push("/dashboard/settings");
} else {
// @ts-ignore
const errorMsg = result?.data?.actionError?.message || result?.data?.actionError?.messageParams?.message || "Failed to update the organization.";
toast.error(errorMsg);
}
},
onError: (error: any) => {
console.error("Mutation network error:", error);
toast.error(error?.message || "A network error occurred.");
},
});
console.log(formatUsersList(props.members))
return (
<Card>
@@ -77,13 +106,13 @@ export const OrganizationForm = (props: organizationFormProps) => {
control={form.control}
name="name"
defaultValue=""
render={({ field }) => (
render={({field}) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Organization 1" {...field} />
</FormControl>
<FormMessage />
<FormMessage/>
</FormItem>
)}
/>
@@ -91,7 +120,7 @@ export const OrganizationForm = (props: organizationFormProps) => {
control={form.control}
name="slug"
defaultValue=""
render={({ field }) => (
render={({field}) => (
<FormItem>
<FormLabel>Slug</FormLabel>
<FormControl>
@@ -104,19 +133,19 @@ export const OrganizationForm = (props: organizationFormProps) => {
}}
/>
</FormControl>
<FormMessage />
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="users"
render={({ field }) => (
render={({field}) => (
<FormItem>
<FormLabel>Users</FormLabel>
<FormControl>
<MultiSelect
options={formatUsersList(props.members)}
options={formatUsersList(props.users)}
onValueChange={field.onChange}
defaultValue={field.value ?? []}
placeholder="Select users"
@@ -126,7 +155,7 @@ export const OrganizationForm = (props: organizationFormProps) => {
/>
</FormControl>
<FormDescription>Select users you want to add to this organization</FormDescription>
<FormMessage />
<FormMessage/>
</FormItem>
)}
/>
@@ -4,14 +4,14 @@ 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 { v4 as uuidv4 } from "uuid";
import { OrganizationFormSchema } from "@/components/wrappers/dashboard/organization/OrganizationForm/organization-form.schema";
import { OrganizationFormSchema } from "@/components/wrappers/dashboard/organization/organization-form/organization-form.schema";
import { db } from "@/db";
import { eq } from "drizzle-orm";
import {checkSlugOrganization, createOrganization, deleteOrganization} from "@/lib/auth/auth";
import {and, eq, inArray} from "drizzle-orm";
import {auth, checkSlugOrganization, createOrganization, deleteOrganization} from "@/lib/auth/auth";
import {slugify} from "@/utils/slugify";
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>> => {
try {
@@ -72,53 +72,117 @@ export const updateOrganizationAction = userAction
organizationId: z.string(),
})
)
.action(async ({ parsedInput }): Promise<ServerActionResult<Organization>> => {
.action(async ({ parsedInput, ctx }): Promise<ServerActionResult<Organization>> => {
try {
const newUserList = parsedInput.data.users;
console.log(parsedInput);
const organization = await db.query.organization.findFirst({
where: eq(drizzleDb.schemas.organization.id, parsedInput.organizationId),
with: {
members: true,
}
});
const organization = await db.select().from(drizzleDb.schemas.organization).where(eq(drizzleDb.schemas.organization.id, parsedInput.organizationId)).execute();
if (organization.length === 0) {
throw new Error("Organization not found.");
if (!organization) {
return {
success: false,
actionError: {
message: "Organization not found.",
status: 404,
cause: "not_found",
},
};
}
const existingItemIds = organization[0].users.map((user) => user.userId);
const existingItemIds = organization.members
.filter((member) => member.userId !== ctx.user.id)
.map((member) => member.userId);
const usersToAdd = newUserList.filter((id) => !existingItemIds.includes(id));
const usersToRemove = existingItemIds.filter((id) => !newUserList.includes(id));
if (usersToAdd.length > 0) {
await db
.insert(drizzleDb.schemas.organization)
.values(
usersToAdd.map((userId) => ({
userId,
organizationId: organization[0].id,
for (const userToAdd of usersToAdd) {
await auth.api.addMember({
body: {
userId: userToAdd,
role: "member",
}))
)
.execute();
organizationId: organization.id,
},
});
}
//
// await db
// .insert(drizzleDb.schemas.member)
// .values(
// usersToAdd.map((userId) => ({
// userId,
// organizationId: organization.id,
// role: "member",
// }))
// )
// .execute();
}
if (usersToRemove.length > 0) {
await db.delete().from(userOrganization).where(inArray(userOrganization.userId, usersToRemove)).execute();
await db.delete(drizzleDb.schemas.member).where(inArray(drizzleDb.schemas.member.userId, usersToRemove)).execute();
// TODO : Do not delete, go permission error with better auth
// for (const userToRemove of usersToRemove) {
//
// const memberToRemove = await db.query.member.findFirst({
// where: and(eq(drizzleDb.schemas.member.userId, userToRemove), eq(drizzleDb.schemas.member.organizationId, organization.id)),
// with: {
// user: true
// }
// })
// console.log(memberToRemove)
//
// if (memberToRemove) {
// console.log("ici")
// await auth.api.removeMember({
// body: {
// memberIdOrEmail: memberToRemove.user.email,
// organizationId: organization.id,
// },
// headers: await headers()
// });
// }
//
// }
}
// const updatedOrganization = await auth.api.updateOrganization({
// body: {
// data: {
// name: parsedInput.data.name,
// slug: parsedInput.data.slug,
// },
// organizationId: organization.id,
// },
// headers: await headers(),
// });
const updatedOrganization = await db
.update(organization)
.update(drizzleDb.schemas.organization)
.set({
name: parsedInput.data.name,
slug: parsedInput.data.slug,
})
.where(eq(organization.id, parsedInput.organizationId))
.where(eq(drizzleDb.schemas.organization.id, parsedInput.organizationId))
.returning()
.execute();
return {
success: true,
value: updatedOrganization[0],
value: updatedOrganization as unknown as Organization,
actionSuccess: {
message: "Organization has been successfully updated.",
messageParams: { organizationId: updatedOrganization[0].id },
messageParams: { organizationId: organization.id},
},
};
} catch (error) {
@@ -128,7 +192,7 @@ export const updateOrganizationAction = userAction
actionError: {
message: "Failed to update organization.",
status: 500,
cause: error.message ?? "Unknown error",
cause: "server_error",
messageParams: { message: "Error updating the organization" },
},
};