Working on add/delete users to workspace. Edit workspaces and some permissions.

This commit is contained in:
charles-gauthereau
2024-12-30 21:10:49 +01:00
parent 379271501d
commit e1a7ca8558
19 changed files with 466 additions and 58 deletions
@@ -0,0 +1,162 @@
"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 {Organization, User} from "@prisma/client"
import {MultiSelect} from "@/components/wrappers/common/multiSelect/MultiSelect";
import {
OrganizationFormSchema,
OrganizationFormType
} from "@/components/wrappers/dashboard/organization/OrganizationForm/organization-form.schema";
import {
createOrganizationAction,
updateOrganizationAction
} from "@/components/wrappers/dashboard/organization/organization.action";
import {toast} from "sonner";
export type organizationFormProps = {
defaultValues?: Organization;
users: User[]
}
export const OrganizationForm = (props: organizationFormProps) => {
const router = useRouter();
const isCreate = !Boolean(props.defaultValues)
const formatUsersList = (users: User[]) => {
return users.map(user => ({
value: user.id,
label: `${user.name} | ${user.email}`,
}));
};
const formatDefaultUsers = (users: OrganizationFormType['users']): string[] => {
console.log(users)
return users.map(user => user.userId );
};
const formattedDefaultValues = {
...props.defaultValues,
users: !isCreate ? formatDefaultUsers(props.defaultValues?.users) : []
}
const form = useZodForm({
schema: OrganizationFormSchema,
defaultValues: formattedDefaultValues,
});
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);
}
}
})
return (
<Card>
<CardHeader>
</CardHeader>
<CardContent>
<Form form={form}
className="flex flex-col gap-4"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
>
<FormField
control={form.control}
name="name"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Organization 1" {...field} />
</FormControl>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="slug"
defaultValue=""
render={({field}) => (
<FormItem>
<FormLabel>Slug</FormLabel>
<FormControl>
<Input
placeholder="project-1" {...field}
onChange={(e) => {
const value = e.target.value.replaceAll(" ", "-").toLowerCase()
field.onChange(value)
}}/>
</FormControl>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="users"
render={({field}) => (
<FormItem>
<FormLabel>Databases</FormLabel>
<FormControl>
<MultiSelect
options={formatUsersList(props.users)}
onValueChange={field.onChange}
defaultValue={field.value ?? []}
placeholder="Select databases"
variant="inverted"
animation={2}
// maxCount={100}
/>
</FormControl>
<FormDescription>Select users you want to add to this organization</FormDescription>
<FormMessage/>
</FormItem>
)
}
/>
<Button>
{isCreate ? `Create Organization` : `Update Organization`}
</Button>
</Form>
</CardContent>
</Card>
)
}
@@ -0,0 +1,14 @@
import {z} from "zod";
export const OrganizationFormSchema = z.object({
name: z.string().min(5, 'Name must be at least 5 characters long').max(40, 'Name must be at most 40 characters long'),
slug: z.string()
.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'),
users: z.array(z.string()),
});
export type OrganizationFormType = z.infer<typeof OrganizationFormSchema>;
@@ -8,6 +8,10 @@ import {Organization} from "@prisma/client";
import {z} from "zod";
import {v4 as uuidv4} from "uuid";
import {db} from "@/db";
import {
OrganizationFormSchema
} from "@/components/wrappers/dashboard/organization/OrganizationForm/organization-form.schema";
import {ProjectSchema} from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm.schema";
const verifySlugUniqueness = async (slug: string) => {
@@ -71,6 +75,95 @@ export const createOrganizationAction = userAction
export const updateOrganizationAction = userAction
.schema(
z.object({
data: OrganizationFormSchema,
organizationId: z.string()
}))
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => {
try {
const newUserList = parsedInput.data.users
const organization = await prisma.organization.findFirst({
where:{
id: parsedInput.organizationId,
},
include: {
users : {}
}
})
const existingItemIds = organization.users.map((user) => user.userId);
const usersToAdd = newUserList.filter(
(id) => !existingItemIds.includes(id)
);
const usersToRemove = existingItemIds.filter(
(id) => !newUserList.includes(id)
);
console.log(usersToAdd);
console.log(usersToRemove);
if (usersToAdd.length > 0) {
await prisma.userOrganization.createMany({
data: usersToAdd.map((userId) => ({
userId: userId,
organizationId: organization.id,
})),
skipDuplicates: true, // Optional: to avoid duplicate insertion errors
});
}
if (usersToRemove.length > 0) {
await prisma.userOrganization.deleteMany({
where: {
userId: { in: usersToRemove },
},
});
}
const updatedOrganization = await prisma.organization.update({
where:{
id: organization.id
},
data:{
name: parsedInput.data.name,
slug: parsedInput.data.slug,
}
})
return {
success: true,
value: updatedOrganization,
actionSuccess: {
message: "Organization has been successfully updated.",
messageParams: {organizationId: updatedOrganization.id},
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to update organization.",
status: 500,
cause: error.message ?? "Unknown error",
messageParams: {message: "Error updating the organization"},
},
};
}
});
export const deleteOrganizationAction = userAction
.schema(z.string())
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => {