migration

This commit is contained in:
Théo LAGACHE
2025-05-16 15:54:17 +02:00
parent 01019fe1a3
commit 6f2523e524
285 changed files with 15492 additions and 46090 deletions
@@ -1,35 +1,24 @@
"use client";
import {Card, CardContent} 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 {TooltipProvider} from "@/components/ui/tooltip";
import { Card, CardContent } 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 { TooltipProvider } from "@/components/ui/tooltip";
import {
EmailFormSchema,
EmailFormType
} from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import Link from "next/link";
import {PasswordInput} from "@/components/wrappers/auth/PaswordInput/password-input";
import {
updateEmailSettingsAction
} from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import { EmailFormSchema, EmailFormType } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import { PasswordInput } from "@/components/wrappers/auth/PaswordInput/password-input";
import { updateEmailSettingsAction } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.action";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
export type EmailFormProps = {
defaultValues?: EmailFormType;
}
};
export const EmailForm = (props: EmailFormProps) => {
const isCreate = !Boolean(props.defaultValues)
const form = useZodForm({
schema: EmailFormSchema,
defaultValues: props.defaultValues,
@@ -38,45 +27,40 @@ export const EmailForm = (props: EmailFormProps) => {
const mutation = useMutation({
mutationFn: async (values: EmailFormType) => {
const updateEmailSettings = await updateEmailSettingsAction({name: "system", data: values})
const data = updateEmailSettings?.data?.data
const updateEmailSettings = await updateEmailSettingsAction({ name: "system", data: values });
const data = updateEmailSettings?.data?.data;
if (updateEmailSettings?.serverError || !data) {
console.log(updateEmailSettings?.serverError);
toast.error(updateEmailSettings?.serverError);
return;
}
toast.success(`Success updating email informations`);
router.refresh()
}
})
router.refresh();
},
});
return (
<TooltipProvider>
<Card>
<CardContent>
<Form form={form}
className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
<Form
form={form}
className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
>
<FormField
control={form.control}
name="smtpFrom"
defaultValue=""
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>From Email *</FormLabel>
<FormControl>
<Input
placeholder={"exemple@portabase.com"} {...field} />
<Input placeholder={"exemple@portabase.com"} {...field} />
</FormControl>
<FormDescription>{"The email from where the email will be send"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
@@ -84,16 +68,14 @@ export const EmailForm = (props: EmailFormProps) => {
control={form.control}
name="smtpHost"
defaultValue=""
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Server Host *</FormLabel>
<FormControl>
<Input
placeholder={"ssl0.ovh.net"} {...field} />
<Input placeholder={"ssl0.ovh.net"} {...field} />
</FormControl>
<FormDescription>{"Your email server host"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
@@ -101,16 +83,14 @@ export const EmailForm = (props: EmailFormProps) => {
control={form.control}
name="smtpPort"
defaultValue=""
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Server Port *</FormLabel>
<FormControl>
<Input
placeholder={"465"} {...field} />
<Input placeholder={"465"} {...field} />
</FormControl>
<FormDescription>{"Your email server port (send)"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
@@ -119,15 +99,15 @@ export const EmailForm = (props: EmailFormProps) => {
control={form.control}
name="smtpPassword"
defaultValue=""
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<PasswordInput placeholder="Password" {...field}/>
<PasswordInput placeholder="Password" {...field} />
</FormControl>
<FormDescription>{"Your email server password"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
@@ -135,29 +115,23 @@ export const EmailForm = (props: EmailFormProps) => {
control={form.control}
name="smtpUser"
defaultValue=""
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>User Email *</FormLabel>
<FormControl>
<Input
placeholder={"exemple@portabase.com"} {...field} />
<Input placeholder={"exemple@portabase.com"} {...field} />
</FormControl>
<FormDescription>{"The email server user"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end gap-4">
<Button>
Save
</Button>
<Button>Save</Button>
</div>
</Form>
</CardContent>
</Card>
</TooltipProvider>
)
}
);
};
@@ -1,31 +1,30 @@
"use server"
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {prisma} from "@/prisma";
import {EmailFormSchema} from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
"use server";
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { EmailFormSchema } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import { setting as drizzleSetting } from "@/db/schema";
import { eq } from "drizzle-orm";
import { db } from "@/db";
export const updateEmailSettingsAction = userAction
.schema(
z.object({
name: z.string(),
data: EmailFormSchema,
}
)
)
.action(async ({parsedInput, ctx}) => {
console.log("parsedInput", parsedInput.data)
const updatedSettings = await prisma.settings.update({
where: {
name: parsedInput.name,
},
data: parsedInput.data,
name: z.string(),
data: EmailFormSchema,
})
)
.action(async ({ parsedInput }) => {
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.set({
...data,
})
.where(eq(drizzleSetting.name, name))
.returning();
return {
data: updatedSettings,
}
})
};
});
@@ -1,4 +1,4 @@
import {z} from "zod";
import { z } from "zod";
export const EmailFormSchema = z.object({
smtpPassword: z.string(),
@@ -1,39 +1,40 @@
import {EmailForm} from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/EmailForm";
import {Settings} from "@prisma/client";
import {Send} from "lucide-react";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import {useMutation} from "@tanstack/react-query";
import {sendEmail} from "@/utils/email-helper";
import { EmailForm } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/EmailForm";
import { Send } from "lucide-react";
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading";
import { useMutation } from "@tanstack/react-query";
import { sendEmail } from "@/utils/email-helper";
import TestEmailSettings from "../../../../../../emails/TestEmailSettings";
import {render} from "@react-email/render";
import {toast} from "sonner";
import { render } from "@react-email/render";
import { toast } from "sonner";
import { Setting } from "@/db/schema";
export type SettingsEmailTabProps = {
settings: Settings
}
settings: Setting;
};
export const SettingsEmailTab = (props: SettingsEmailTabProps) => {
const mutation = useMutation({
mutationFn: async () => {
if (!props.settings.smtpUser || !props.settings.smtpFrom) {
toast.error("SMTP is not configured");
return;
}
const email = await sendEmail({
to: props.settings.smtpUser,
subject: "Portabase",
html: await render(TestEmailSettings(),{})
html: await render(TestEmailSettings(), {}),
from: props.settings.smtpFrom,
});
if(email.response){
if (email.response) {
toast.success("Test Email Successfully sent !");
}
}
})
},
});
const handleSendMailTest = async () => {
await mutation.mutateAsync()
}
await mutation.mutateAsync();
};
return (
<div className="flex flex-col h-full py-4">
@@ -43,18 +44,17 @@ export const SettingsEmailTab = (props: SettingsEmailTabProps) => {
<ButtonWithLoading
isPending={mutation.isPending}
onClick={async () => {
await handleSendMailTest()
await handleSendMailTest();
}}
icon={<Send/>}
icon={<Send />}
text="Send email test"
size="default"
/>
)}
</div>
<div className="mt-5">
<EmailForm defaultValues={props.settings.smtpFrom ? props.settings : null}/>
<EmailForm defaultValues={props.settings.smtpFrom ? props.settings : null} />
</div>
</div>
)
}
);
};
@@ -1,78 +1,75 @@
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
import {Info, Send, ShieldCheck} from "lucide-react";
import {Switch} from "@/components/ui/switch";
import {Label} from "@/components/ui/label";
import {StorageS3Form} from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/StorageS3Form";
import {useState} from "react";
import {Settings} from "@prisma/client";
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
import {useMutation} from "@tanstack/react-query";
import {checkConnexionToS3} from "@/features/upload/public/upload.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {
updateStorageSettingsAction
} from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Info, ShieldCheck } from "lucide-react";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { StorageS3Form } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/StorageS3Form";
import { useState } from "react";
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading";
import { useMutation } from "@tanstack/react-query";
import { checkConnexionToS3 } from "@/features/upload/public/upload.action";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import { updateStorageSettingsAction } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import { Setting } from "@/db/schema";
export type SettingsStorageTabProps = {
settings: Settings
}
settings: Setting;
};
export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
const router = useRouter()
const router = useRouter();
const mutation = useMutation({
mutationFn: async () => {
const result = await checkConnexionToS3()
if(result.error){
toast.error("An error occured during the connexion !")
}else{
toast.success("Connexion succeed!")
const result = await checkConnexionToS3();
if (result.error) {
toast.error("An error occured during the connexion !");
} else {
toast.success("Connexion succeed!");
}
}
})
},
});
const [isSwitched, setIsSwitched] = useState<boolean>(props.settings.storage !== "local");
const updateMutation = useMutation({
mutationFn: () => updateStorageSettingsAction({name: "system", data: {storage: isSwitched ? "s3": "local"}}),
mutationFn: () => updateStorageSettingsAction({ name: "system", data: { storage: isSwitched ? "s3" : "local" } }),
onSuccess: () => {
toast.success(`Settings updated successfully.`);
router.refresh()
router.refresh();
},
onError: () => {
toast.error(`An error occurred while updating settings information.`);
},
});
const HandleSwitchStorage = async () => {
setIsSwitched(!isSwitched);
await updateMutation.mutateAsync()
}
await updateMutation.mutateAsync();
};
return (
<div className="flex flex-col h-full py-4">
<h1>Settings for Portabase storage</h1>
<Alert className="mt-3">
<Info className="h-4 w-4"/>
<Info className="h-4 w-4" />
<AlertTitle>Informations</AlertTitle>
<AlertDescription>
Actually you can only store you data in one place : s3 compatible or in local.
For exemple you cannot choose to store images in one place and .dump files in another.
Actually you can only store you data in one place : s3 compatible or in local. For exemple you cannot choose to store images in one place
and .dump files in another.
</AlertDescription>
</Alert>
<div className="flex flex-col h-full py-4 ">
<div className="flex items-center justify-between space-x-2">
<div className="flex items-center space-x-2">
<Label htmlFor="storage-mode">Storage Mode (Local/s3 compatible)</Label>
<Switch
checked={isSwitched}
onCheckedChange={async () => {
await HandleSwitchStorage()
}}
id="storage-mode"/>
<Label htmlFor="storage-mode">Storage Mode (Local/s3 compatible)</Label>
<Switch
checked={isSwitched}
onCheckedChange={async () => {
await HandleSwitchStorage();
}}
id="storage-mode"
/>
</div>
<div>
<ButtonWithLoading
@@ -80,7 +77,7 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
disabled={!isSwitched}
isPending={mutation.isPending}
onClick={async () => {
await mutation.mutateAsync()
await mutation.mutateAsync();
}}
icon={<ShieldCheck />}
text="Test connexion"
@@ -89,10 +86,10 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
</div>
{isSwitched && (
<div className="mt-5">
<StorageS3Form defaultValues={props.settings.s3EndPointUrl ? props.settings : null}/>
<StorageS3Form defaultValues={props.settings.s3EndPointUrl ? props.settings : null} />
</div>
)}
</div>
</div>
)
}
);
};
@@ -1,28 +1,21 @@
"use client";
import {Card, CardContent} 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 {TooltipProvider} from "@/components/ui/tooltip";
import { Card, CardContent } 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 { TooltipProvider } from "@/components/ui/tooltip";
import {
S3FormSchema,
S3FormType
} from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import {useRouter} from "next/navigation";
import {toast} from "sonner";
import {
updateS3SettingsAction
} from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import { S3FormSchema, S3FormType } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { updateS3SettingsAction } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
export type S3FormProps = {
defaultValues?: S3FormType;
}
};
export const StorageS3Form = (props: S3FormProps) => {
const form = useZodForm({
@@ -34,98 +27,92 @@ export const StorageS3Form = (props: S3FormProps) => {
const mutation = useMutation({
mutationFn: async (values: S3FormType) => {
console.log(values)
const updateS3Settings = await updateS3SettingsAction({name: "system", data: values})
const data = updateS3Settings?.data?.data
console.log(values);
const updateS3Settings = await updateS3SettingsAction({ name: "system", data: values });
const data = updateS3Settings?.data?.data;
if (updateS3Settings?.serverError || !data) {
console.log(updateS3Settings?.serverError);
toast.error(updateS3Settings?.serverError);
return;
}
toast.success(`Success updating storage informations`);
router.refresh()
}
})
router.refresh();
},
});
return (
<TooltipProvider>
<Card>
<CardContent>
<Form form={form}
className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
<Form
form={form}
className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
>
<FormField
control={form.control}
name="s3EndPointUrl"
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Endpoint Url *</FormLabel>
<FormControl>
<Input
placeholder={"s3.eu-west-3.amazonaws.com"} {...field} />
<Input placeholder={"s3.eu-west-3.amazonaws.com"} {...field} />
</FormControl>
<FormDescription>{"Your s3 compatible url"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="s3AccessKeyId"
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Access Key *</FormLabel>
<FormControl>
<Input
placeholder={"The access key token"} {...field} />
<Input placeholder={"The access key token"} {...field} />
</FormControl>
<FormDescription>{"Add your access key"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="s3SecretAccessKey"
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Secret Key *</FormLabel>
<FormControl>
<Input
placeholder={"The secret key token"} {...field} />
<Input placeholder={"The secret key token"} {...field} />
</FormControl>
<FormDescription>{"Add your secret key"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="S3BucketName"
render={({field}) => (
render={({ field }) => (
<FormItem>
<FormLabel>Bucket name *</FormLabel>
<FormControl>
<Input
placeholder={"my-bucket"} {...field} />
<Input placeholder={"my-bucket"} {...field} />
</FormControl>
<FormDescription>{"The bucket name where you want to store your data"}</FormDescription>
<FormMessage/>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end gap-4">
<Button>
Save
</Button>
<Button>Save</Button>
</div>
</Form>
</CardContent>
</Card>
</TooltipProvider>
)
}
);
};
@@ -1,51 +1,50 @@
"use server"
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {prisma} from "@/prisma";
import {
S3FormSchema,
StorageSwitchSchema
} from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
"use server";
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { db } from "@/db";
import { setting as drizzleSetting } from "@/db/schema";
import { S3FormSchema, StorageSwitchSchema } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import { eq } from "drizzle-orm";
export const updateS3SettingsAction = userAction
.schema(
z.object({
name: z.string(),
data: S3FormSchema,
}
)
)
.action(async ({parsedInput, ctx}) => {
const updatedSettings = await prisma.settings.update({
where: {
name: parsedInput.name,
},
data: parsedInput.data,
name: z.string(),
data: S3FormSchema,
})
)
.action(async ({ parsedInput }) => {
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.returning();
return {
data: updatedSettings,
}
})
};
});
export const updateStorageSettingsAction = userAction
.schema(
z.object({
name: z.string(),
data: StorageSwitchSchema,
}
)
)
.action(async ({parsedInput, ctx}) => {
const updatedSettings = await prisma.settings.update({
where: {
name: parsedInput.name,
},
data: parsedInput.data,
name: z.string(),
data: StorageSwitchSchema,
})
)
.action(async ({ parsedInput }) => {
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.returning();
return {
data: updatedSettings,
}
})
};
});
@@ -1,4 +1,4 @@
import {z} from "zod";
import { z } from "zod";
export const S3FormSchema = z.object({
s3EndPointUrl: z.string(),
@@ -9,9 +9,8 @@ export const S3FormSchema = z.object({
export type S3FormType = z.infer<typeof S3FormSchema>;
export const StorageSwitchSchema = z.object({
storage: z.string(),
})
storage: z.enum(["local", "s3"]),
});
export type StorageType= z.infer<typeof StorageSwitchSchema>;
export type StorageType = z.infer<typeof StorageSwitchSchema>;
@@ -1,42 +1,43 @@
"use client"
import {User, Settings} from "@prisma/client";
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {SettingsEmailTab} from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
import {SettingsStorageTab} from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
import {AdminUsersTable} from "@/components/wrappers/dashboard/admin/admin-user-table";
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { SettingsEmailTab } from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
import { SettingsStorageTab } from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
import { AdminUsersTable } from "@/components/wrappers/dashboard/admin/admin-user-table";
import { Setting } from "@/db/schema";
import { User } from "@/db/schema/01_user";
export type AdminTabsProps = {
currentUser: User;
users: User[];
settings: Settings;
}
settings: Setting;
};
export const AdminTabs = (props: AdminTabsProps) => {
const {currentUser, users, settings} = props;
const { users, settings } = props;
return (
<Tabs defaultValue="users">
<TabsList className="w-full">
<TabsTrigger className="w-full " value="users">Users</TabsTrigger>
<TabsTrigger className="w-full " value="email">Email</TabsTrigger>
<TabsTrigger className="w-full " value="storage">Storage</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="users">
<AdminUsersTable currentUser={currentUser} users={users}/>
<AdminUsersTable users={users} />
</TabsContent>
<TabsContent value="email">
<SettingsEmailTab settings={settings}/>
<SettingsEmailTab settings={settings} />
</TabsContent>
<TabsContent value="storage">
<SettingsStorageTab settings={settings}/>
<SettingsStorageTab settings={settings} />
</TabsContent>
</Tabs>
)
}
);
};
@@ -1,95 +1,21 @@
import {flexRender, Row, RowData} from "@tanstack/react-table";
import {User, UserOrganization} from "@prisma/client";
import {DataTableWithPagination} from "@/components/wrappers/common/table/data-table-with-pagination";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {cn} from "@/lib/utils";
import {usersColumnsAdmin} from "@/components/wrappers/dashboard/admin/columns-users";
import { usersColumnsAdmin } from "@/components/wrappers/dashboard/admin/columns-users";
import { User } from "@/db/schema/01_user";
import { DataTable } from "../../common/table/data-table";
export type AdminUsersTableProps = {
currentUser: User;
users: User[]
}
users: User[];
};
export const AdminUsersTable = (props: AdminUsersTableProps) => {
const {currentUser, users} = props;
const { users } = props;
return (
<div className="flex flex-col h-full py-4">
<div className="flex gap-4 h-fit justify-between">
<h1>List of Portabase's users</h1>
</div>
<div className="mt-5">
<DataTableWithPagination
columns={usersColumnsAdmin}
data={users}
DataTable={UsersDataTableAdmin}
dataTableProps={{currentUser}}
/>
<DataTable columns={usersColumnsAdmin} data={users} />
</div>
</div>
)
}
export type usersDataTableProps = {
currentUser: User;
table: any,
}
export const UsersDataTableAdmin = ({currentUser, table}: usersDataTableProps) => {
return (
<div className="rounded-md border w-full ">
<Table className="w-full">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row: Row<User>) => {
return(
<TableRow
className={cn((row.original.id) === currentUser.id ? "opacity-40 pointer-events-none" : "")}
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
)
}
)) : (
<TableRow>
<TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}
);
};
@@ -1,31 +1,28 @@
"use client"
"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";
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 usersColumnsAdmin: ColumnDef<User>[] = [
{
accessorKey: "role",
header: "Role",
cell: ({row}) => {
const router = useRouter();
const [role, setRole] = useState<string>(row.getValue("role"))
cell: ({ row }) => {
const [role, setRole] = useState<string>(row.getValue("role"));
const updateMutation = useMutation({
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
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.`);
@@ -33,55 +30,52 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
});
const handleUpdateRole = async () => {
const nextRole = role === "admin" ? "pending"
: role === "pending" ? "user"
: "admin";
const nextRole = role === "admin" ? "pending" : role === "pending" ? "user" : "admin";
setRole(nextRole);
await updateMutation.mutateAsync()
await updateMutation.mutateAsync();
};
return <Badge
className="cursor-pointer"
onClick={() => handleUpdateRole()}
variant="outline">{role}</Badge>
return (
<Badge className="cursor-pointer" onClick={() => handleUpdateRole()} variant="outline">
{role}
</Badge>
);
},
},
{
accessorKey: "name",
header: "Name"
header: "Name",
},
{
accessorKey: "email",
header: "Email"
header: "Email",
},
{
accessorKey: "updatedAt",
header: "Updated At",
cell: ({row}) => {
cell: ({ row }) => {
return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR");
},
},
{
accessorKey: "authMethod",
header: "Method",
cell: ({row}) => {
return <Badge variant="outline">{row.getValue("authMethod")}</Badge>
cell: ({ row }) => {
return <Badge variant="outline">{row.getValue("authMethod")}</Badge>;
},
},
{
header: "Action",
id: "actions",
cell: ({ row, table }) => {
cell: ({ row }) => {
const router = useRouter();
const mutation = useMutation({
mutationFn: () => deleteUserAction(row.original.id),
onSuccess: async () => {
toast.success('User deleted successfully.');
router.refresh()
toast.success("User deleted successfully.");
router.refresh();
},
})
});
return (
<div className="flex items-center gap-2">
@@ -94,9 +88,8 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
}}
size="icon"
/>
</div>
);
},
},
]
];