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
@@ -8,11 +8,21 @@ import {ButtonDeleteProject} from "@/components/wrappers/dashboard/projects/Butt
import {CardsWithPagination} from "@/components/wrappers/common/cards-with-pagination"; import {CardsWithPagination} from "@/components/wrappers/common/cards-with-pagination";
import {ProjectDatabaseCard} from "@/components/wrappers/dashboard/projects/ProjectCard/ProjectDatabaseCard"; import {ProjectDatabaseCard} from "@/components/wrappers/dashboard/projects/ProjectCard/ProjectDatabaseCard";
import {notFound} from "next/navigation"; import {notFound} from "next/navigation";
import {getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
export default async function RoutePage(props: PageParams<{ projectId: string }>) { export default async function RoutePage(props: PageParams<{ slug:string, projectId: string }>) {
const {slug: organizationSlug, projectId} = await props.params
const currentOrganizationSlug = await getCurrentOrganizationSlug()
if(currentOrganizationSlug != organizationSlug) {
notFound()
}
const {projectId} = await props.params
const project = await prisma.project.findUnique({ const project = await prisma.project.findUnique({
where: { where: {
@@ -33,7 +43,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
<PageTitle className="flex items-center"> <PageTitle className="flex items-center">
{project.name} {project.name}
<Link className={buttonVariants({variant: "outline"})} <Link className={buttonVariants({variant: "outline"})}
href={`/dashboard/projects/${project.id}/edit`}> href={`/dashboard/${currentOrganizationSlug}/projects/${project.id}/edit`}>
<GearIcon className="w-7 h-7"/> <GearIcon className="w-7 h-7"/>
</Link> </Link>
</PageTitle> </PageTitle>
@@ -51,6 +61,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
{project.databases.length > 0 ? {project.databases.length > 0 ?
<CardsWithPagination <CardsWithPagination
data={project.databases} data={project.databases}
organizationSlug={organizationSlug}
cardItem={ProjectDatabaseCard} cardItem={ProjectDatabaseCard}
cardsPerPage={4} cardsPerPage={4}
numberOfColumns={1} numberOfColumns={1}
@@ -2,9 +2,17 @@ import {PageParams} from "@/types/next";
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page"; import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {prisma} from "@/prisma"; import {prisma} from "@/prisma";
import {ProjectForm} from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm"; import {ProjectForm} from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm";
import {notFound} from "next/navigation";
import {getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{slug: string}>) {
const {slug: organizationSlug} = await props.params
const currentOrganizationSlug = await getCurrentOrganizationSlug()
if(currentOrganizationSlug != organizationSlug) {
notFound()
}
const availableDatabases = await prisma.database.findMany({ const availableDatabases = await prisma.database.findMany({
where: { where: {
@@ -20,7 +28,7 @@ export default async function RoutePage(props: PageParams<{}>) {
const organization = await prisma.organization.findFirst({ const organization = await prisma.organization.findFirst({
where: { where: {
slug: 'default', slug: currentOrganizationSlug,
} }
}) })
@@ -47,7 +47,7 @@ export default async function RoutePage(props: PageParams<{slug: string}>) {
</PageTitle> </PageTitle>
{projects.length > 0 && ( {projects.length > 0 && (
<PageActions> <PageActions>
<Link href={"/dashboard/projects/new"}> <Link href={`/dashboard/${currentOrganizationSlug}/projects/new`}>
<Button>+ Create Project</Button> <Button>+ Create Project</Button>
</Link> </Link>
</PageActions> </PageActions>
@@ -59,6 +59,7 @@ export default async function RoutePage(props: PageParams<{slug: string}>) {
{projects.length > 0 ? {projects.length > 0 ?
<CardsWithPagination <CardsWithPagination
organizationSlug={currentOrganizationSlug}
data={projects} data={projects}
cardItem={ProjectCard} cardItem={ProjectCard}
cardsPerPage={4} cardsPerPage={4}
@@ -66,7 +67,7 @@ export default async function RoutePage(props: PageParams<{slug: string}>) {
/> />
: :
<Link <Link
href="/dashboard/projects/new" href={`/dashboard/${currentOrganizationSlug}/projects/new`}
className=" flex item-center justify-center border-2 border-dashed transition-colors border-primary p-8 lg:p-12 w-full rounded-md"> className=" flex item-center justify-center border-2 border-dashed transition-colors border-primary p-8 lg:p-12 w-full rounded-md">
Create new Project Create new Project
</Link> </Link>
@@ -0,0 +1,53 @@
import {PageParams} from "@/types/next";
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
import {prisma} from "@/prisma";
import {requiredCurrentUser} from "@/auth/current-user";
import {notFound} from "next/navigation";
import {OrganizationForm} from "@/components/wrappers/dashboard/organization/OrganizationForm/OrganizationForm";
export default async function RoutePage(props: PageParams<{
slug: string;
}>) {
const {slug: organizationSlug} = await props.params
const currentOrganizationSlug = await getCurrentOrganizationSlug()
if(currentOrganizationSlug != organizationSlug) {
notFound()
}
const organization = await prisma.organization.findUnique({
where:{
slug: currentOrganizationSlug,
},
include:{
users:{
include:{
user:{}
}
}
}
})
const users = await prisma.user.findMany({
where: {
deleted: {not: true},
}
})
return(
<Page>
<PageHeader>
<PageTitle>
Edit {organization.name}
</PageTitle>
</PageHeader>
<PageContent>
<OrganizationForm users={users} defaultValues={organization}/>
</PageContent>
</Page>
)
}
@@ -4,11 +4,14 @@ import {Page, PageActions, PageContent, PageDescription, PageHeader, PageTitle}
import {requiredCurrentUser} from "@/auth/current-user"; import {requiredCurrentUser} from "@/auth/current-user";
import {SettingsTabs} from "@/components/wrappers/dashboard/settings/SettingsTabs/SettingsTabs"; import {SettingsTabs} from "@/components/wrappers/dashboard/settings/SettingsTabs/SettingsTabs";
import {getCurrentOrganizationId, getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie"; import {getCurrentOrganizationId, getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
import {Button} from "@/components/ui/button"; import {Button, buttonVariants} from "@/components/ui/button";
import {ButtonWithConfirm} from "@/components/wrappers/common/button/button-with-confirm"; import {ButtonWithConfirm} from "@/components/wrappers/common/button/button-with-confirm";
import { import {
DeleteOrganizationButton DeleteOrganizationButton
} from "@/components/wrappers/dashboard/organization/DeleteOrganization/DeleteOrganizationButton"; } from "@/components/wrappers/dashboard/organization/DeleteOrganization/DeleteOrganizationButton";
import {GearIcon} from "@radix-ui/react-icons";
import Link from "next/link";
import {EditButtonSettings} from "@/components/wrappers/dashboard/settings/EditButtonSettings/EditButtonSettings";
export default async function RoutePage(props: PageParams<{}>) { export default async function RoutePage(props: PageParams<{}>) {
@@ -17,7 +20,7 @@ export default async function RoutePage(props: PageParams<{}>) {
const currentOrganizationSlug = await getCurrentOrganizationSlug() const currentOrganizationSlug = await getCurrentOrganizationSlug()
const organization = await prisma.organization.findUnique({ const organization = await prisma.organization.findUnique({
where:{ where: {
slug: currentOrganizationSlug, slug: currentOrganizationSlug,
} }
}) })
@@ -35,6 +38,7 @@ export default async function RoutePage(props: PageParams<{}>) {
}) })
const settings = await prisma.settings.findUnique({ const settings = await prisma.settings.findUnique({
where: { where: {
name: "system" name: "system"
@@ -44,12 +48,16 @@ export default async function RoutePage(props: PageParams<{}>) {
return ( return (
<Page> <Page>
<PageHeader> <PageHeader>
<PageTitle> <PageTitle className="flex items-center">
Settings Settings
{organization.slug != "default" ?
<EditButtonSettings/>
: null}
</PageTitle> </PageTitle>
<PageActions> <PageActions>
{/*<Button variant="destructive">Delete Organization</Button>*/} {organization.slug != "default" ?
<DeleteOrganizationButton organizationSlug={currentOrganizationSlug}/> <DeleteOrganizationButton organizationSlug={currentOrganizationSlug}/>
: null}
</PageActions> </PageActions>
</PageHeader> </PageHeader>
<PageDescription> <PageDescription>
-1
View File
@@ -38,7 +38,6 @@ export default async function RoutePage(props: PageParams<{}>) {
</PageHeader> </PageHeader>
<PageContent className="mt-10"> <PageContent className="mt-10">
<AdminTabs settings={settings} currentUser={user} users={users}/> <AdminTabs settings={settings} currentUser={user} users={users}/>
</PageContent> </PageContent>
</Page> </Page>
) )
@@ -7,6 +7,7 @@ import {PaginationNavigation} from "@/components/wrappers/common/pagination/pagi
export type cardsWithPaginationProps = { export type cardsWithPaginationProps = {
className?: string className?: string
organizationSlug?:string
data: Array<{}>; data: Array<{}>;
cardItem: React.ComponentType; cardItem: React.ComponentType;
cardsPerPage?: number cardsPerPage?: number
@@ -18,7 +19,7 @@ export type cardsWithPaginationProps = {
export const CardsWithPagination = (props: cardsWithPaginationProps) => { export const CardsWithPagination = (props: cardsWithPaginationProps) => {
const {className, data, cardItem, cardsPerPage = 5, numberOfColumns = 1, maxVisiblePages = 3} = props const {className,organizationSlug, data, cardItem, cardsPerPage = 5, numberOfColumns = 1, maxVisiblePages = 3} = props
const CardItem = cardItem const CardItem = cardItem
@@ -45,7 +46,7 @@ export const CardsWithPagination = (props: cardsWithPaginationProps) => {
<div className={cn("flex flex-col h-full justify-between", className)}> <div className={cn("flex flex-col h-full justify-between", className)}>
<div className={cn(`grid h-max auto-rows-min gap-4 md:grid-cols-${numberOfColumns}`)}> <div className={cn(`grid h-max auto-rows-min gap-4 md:grid-cols-${numberOfColumns}`)}>
{currentCards.map((card, key) => ( {currentCards.map((card, key) => (
<CardItem key={key} data={card} extendedProps={props.extendedProps} /> <CardItem key={key} data={card} organizationSlug={organizationSlug} extendedProps={props.extendedProps} />
))} ))}
</div> </div>
<PaginationNavigation <PaginationNavigation
@@ -2,9 +2,9 @@ import {flexRender, Row, RowData} from "@tanstack/react-table";
import {User} from "@prisma/client"; import {User} from "@prisma/client";
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination"; import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table"; import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {cn} from "@/lib/utils"; import {cn} from "@/lib/utils";
import {usersColumnsAdmin} from "@/components/wrappers/dashboard/admin/columns-users";
export type AdminUsersTableProps = { export type AdminUsersTableProps = {
currentUser: User; currentUser: User;
@@ -14,7 +14,6 @@ export type AdminUsersTableProps = {
export const AdminUsersTable = (props: AdminUsersTableProps) => { export const AdminUsersTable = (props: AdminUsersTableProps) => {
const {currentUser, users} = props; const {currentUser, users} = props;
return ( return (
<div className="flex flex-col h-full py-4"> <div className="flex flex-col h-full py-4">
<div className="flex gap-4 h-fit justify-between"> <div className="flex gap-4 h-fit justify-between">
@@ -22,7 +21,7 @@ export const AdminUsersTable = (props: AdminUsersTableProps) => {
</div> </div>
<div className="mt-5"> <div className="mt-5">
<DataTableWithPagination <DataTableWithPagination
columns={usersColumns} columns={usersColumnsAdmin}
data={users} data={users}
DataTable={UsersDataTable} DataTable={UsersDataTable}
dataTableProps={{currentUser}} dataTableProps={{currentUser}}
@@ -12,7 +12,7 @@ import {Trash2} from "lucide-react";
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action"; import {deleteUserAction} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading"; import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
export const usersColumns: ColumnDef<User>[] = [ export const usersColumnsAdmin: ColumnDef<User>[] = [
{ {
accessorKey: "role", accessorKey: "role",
header: "Role", header: "Role",
@@ -55,10 +55,10 @@ export const usersColumns: ColumnDef<User>[] = [
header: "Email" header: "Email"
}, },
{ {
accessorKey: "createdAt", accessorKey: "updatedAt",
header: "Created At", header: "Updated At",
cell: ({row}) => { cell: ({row}) => {
return new Date(row.getValue("createdAt")).toLocaleString("fr-FR"); return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR");
}, },
}, },
{ {
@@ -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 {z} from "zod";
import {v4 as uuidv4} from "uuid"; import {v4 as uuidv4} from "uuid";
import {db} from "@/db"; 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) => { 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 export const deleteOrganizationAction = userAction
.schema(z.string()) .schema(z.string())
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => { .action(async ({parsedInput, ctx}): Promise<ServerActionResult<Organization>> => {
@@ -4,16 +4,17 @@ import {Card, CardContent, CardHeader} from "@/components/ui/card";
import Link from "next/link"; import Link from "next/link";
export type projectCardProps = { export type projectCardProps = {
data: any data: any,
organizationSlug?: string
} }
export const ProjectCard = (props: projectCardProps) => { export const ProjectCard = (props: projectCardProps) => {
const {data: project} = props; const {data: project, organizationSlug} = props;
return ( return (
<Link href={`/dashboard/projects/${project.id}`}> <Link href={`/dashboard/${organizationSlug}/projects/${project.id}`}>
<Card className="flex flex-row justify-between"> <Card className="flex flex-row justify-between">
<div className=""> <div className="">
<CardHeader>{project.name}</CardHeader> <CardHeader>{project.name}</CardHeader>
@@ -10,14 +10,15 @@ import {formatDateLastContact} from "@/utils/date-formatting";
export type projectDatabaseCardProps = { export type projectDatabaseCardProps = {
data: Database, data: Database,
extendedProps: any extendedProps: any
organizationSlug: string
} }
export const ProjectDatabaseCard = (props: projectDatabaseCardProps) => { export const ProjectDatabaseCard = (props: projectDatabaseCardProps) => {
const {data: database, extendedProps: extendedProps} = props; const {organizationSlug,data: database, extendedProps: extendedProps} = props;
return ( return (
<Link href={`/dashboard/projects/${extendedProps.id}/database/${database.id}`}> <Link href={`/dashboard/${organizationSlug}/projects/${extendedProps.id}/database/${database.id}`}>
<DatabaseCard data={database}/> <DatabaseCard data={database}/>
</Link> </Link>
) )
@@ -77,7 +77,7 @@ export const ProjectForm = (props: projectFormProps) => {
if (project.data.success) { if (project.data.success) {
toast.success(project.data.actionSuccess.message); toast.success(project.data.actionSuccess.message);
router.push(`/dashboard/projects/${project.data.value.id}`); router.push(`/dashboard/${props.organization.slug}/projects/${project.data.value.id}`);
router.refresh() router.refresh()
} else { } else {
toast.success(project.data.actionError.message); toast.success(project.data.actionError.message);
@@ -0,0 +1,21 @@
"use client"
import {buttonVariants} from "@/components/ui/button";
import {GearIcon} from "@radix-ui/react-icons";
import Link from "next/link";
import {usePathname} from "next/navigation";
export type EditButtonSettings = {}
export const EditButtonSettings= (props:EditButtonSettings) => {
const pathname = usePathname();
return(
<Link className={buttonVariants({variant: "outline"})}
href={`${pathname}/edit/`}
>
<GearIcon className="w-7 h-7"/>
</Link>
)
}
@@ -2,7 +2,7 @@
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs"; import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination"; import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users"; import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users-settings";
import {User, Settings} from "@prisma/client"; import {User, Settings} from "@prisma/client";
import {backupColumns} from "@/features/backup/columns"; import {backupColumns} from "@/features/backup/columns";
import {SettingsEmailTab} from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab"; import {SettingsEmailTab} from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
@@ -21,34 +21,6 @@ export const SettingsTabs = (props: SettingsTabsProps) => {
const {currentUser, users} = props; const {currentUser, users} = props;
return ( return (
// <Tabs defaultValue="informations" >
// <TabsList className="w-full">
// <TabsTrigger className="w-full " value="informations">Info</TabsTrigger>
// <TabsTrigger className="w-full " value="users">Users</TabsTrigger>
// <TabsTrigger className="w-full " value="email">Email</TabsTrigger>
// <TabsTrigger className="w-full " value="storage">Storage</TabsTrigger>
// </TabsList>
// <TabsContent value="informations">
// <div className="flex flex-1 flex-col gap-4 py-4">
// <div className="grid auto-rows-min gap-4 md:grid-cols-3">
// <div className="aspect-video rounded-xl bg-muted/50"/>
// <div className="aspect-video rounded-xl bg-muted/50"/>
// <div className="aspect-video rounded-xl bg-muted/50"/>
// </div>
// <div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min"/>
// </div>
// </TabsContent>
// <TabsContent value="users" >
<SettingsUsersTab currentUser={currentUser} users={users}/> <SettingsUsersTab currentUser={currentUser} users={users}/>
// </TabsContent>
// <TabsContent value="email">
// <SettingsEmailTab settings={props.settings}/>
// </TabsContent>
// <TabsContent value="storage">
// <SettingsStorageTab settings={props.settings}/>
// </TabsContent>
// </Tabs>
) )
} }
@@ -1,6 +1,6 @@
import {User} from "@prisma/client"; import {User} from "@prisma/client";
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination"; import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users"; import {usersColumns} from "@/components/wrappers/dashboard/settings/SettingsUsersTab/columns-users-settings";
import {UsersDataTable} from "@/components/wrappers/dashboard/admin/admin-user-table"; import {UsersDataTable} from "@/components/wrappers/dashboard/admin/admin-user-table";
@@ -0,0 +1,64 @@
"use client"
import {ColumnDef} from "@tanstack/react-table"
import {Badge} from "@/components/ui/badge";
import {User} from "@prisma/client";
import {updateUserAction} from "@/components/wrappers/dashboard/profile/UserForm/user-form.action";
import {useMutation} from "@tanstack/react-query";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {useState} from "react";
import {Trash2} from "lucide-react";
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
export const usersColumns: ColumnDef<User>[] = [
{
accessorKey: "role",
header: "Role",
cell: ({row}) => {
const router = useRouter();
const [role, setRole] = useState<string>(row.getValue("role"))
const updateMutation = useMutation({
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
onSuccess: () => {
toast.success(`User updated successfully.`);
router.refresh()
},
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()
};
return <Badge
className="cursor-pointer"
onClick={() => handleUpdateRole()}
variant="outline">{role}</Badge>
},
},
{
accessorKey: "name",
header: "Name"
},
{
accessorKey: "email",
header: "Email"
},
{
accessorKey: "updatedAt",
header: "Updated At",
cell: ({row}) => {
return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR");
},
}
]