Working on settings Page. Adding init function on next.js startup.

This commit is contained in:
charles-gauthereau
2024-11-16 22:00:22 +01:00
parent b8218dcf85
commit b9a56188ae
16 changed files with 382 additions and 26 deletions
+10 -7
View File
@@ -6,16 +6,19 @@ import {prisma} from "@/prisma";
export default async function Layout({children}: { children: React.ReactNode }) {
const user = await currentUser()
// const userInfo = await prisma.user.findUnique({
// where: {
// email: user?.email
// }
// })
//
if(user){
redirect('/dashboard')
const userInfo = await prisma.user.findUnique({
where: {
email: user?.email
}
})
if(userInfo){
redirect('/dashboard')
}
}
return (
<LayoutAdmin>
<div
+26 -11
View File
@@ -16,20 +16,35 @@ export default async function RoutePage(props: PageParams<{}>) {
<PageTitle>
Agents
</PageTitle>
<PageActions>
<Link href={"/dashboard/agents/new"}>
<Button>+ Create Agent</Button>
</Link>
</PageActions>
{agents.length > 0 && (
<PageActions>
<Link href={"/dashboard/agents/new"}>
<Button>+ Create Agent</Button>
</Link>
</PageActions>
)}
</PageHeader>
<PageContent className="mt-10">
<CardsWithPagination
data={agents}
cardItem={AgentCard}
cardsPerPage={4}
numberOfColumns={1}
/>
{agents.length > 0 ?
<CardsWithPagination
data={agents}
cardItem={AgentCard}
cardsPerPage={4}
numberOfColumns={1}
/>
:
<Link
href="/dashboard/agents/new"
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 Agent
</Link>
}
</PageContent>
</Page>
)
+6 -1
View File
@@ -19,6 +19,11 @@ export default async function RoutePage(props: PageParams<{}>) {
}
})
const settings = await prisma.settings.findUnique({
where:{
name: "system"
}
})
return (
<Page>
@@ -31,7 +36,7 @@ export default async function RoutePage(props: PageParams<{}>) {
Manage your Portabase settings
</PageDescription>
<PageContent>
<SettingsTabs users={users}/>
<SettingsTabs settings={settings} users={users}/>
</PageContent>
</Page>
)
+6
View File
@@ -0,0 +1,6 @@
import {init} from "@/utils/init";
export async function register() {
init()
}
@@ -0,0 +1,8 @@
/*
Warnings:
- Added the required column `name` to the `Settings` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Settings" ADD COLUMN "name" TEXT NOT NULL;
@@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[name]` on the table `Settings` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "Settings_name_key" ON "Settings"("name");
+1
View File
@@ -135,6 +135,7 @@ enum TypeStorage {
model Settings {
id String @id @default(cuid())
storage TypeStorage @default(local)
name String @unique
s3EndPointUrl String?
s3AccessKeyId String?
s3SecretAccessKey String?
@@ -0,0 +1,66 @@
"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 {
EmailFormSchema,
EmailFormType
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
export type EmailFormProps = {
defaultValues?: EmailFormType;
}
export const EmailForm = (props: EmailFormProps) => {
const form = useZodForm({
schema: EmailFormSchema,
});
const mutation = useMutation({
mutationFn: async (values: EmailFormType) => {
}
})
return (
<TooltipProvider>
<Card>
<CardContent>
<Form form={form}
className="flex flex-col gap-4 mt-3"
onSubmit={async (values) => {
await mutation.mutateAsync(values);
}}
>
<FormField
control={form.control}
name="smtpFrom"
render={({field}) => (
<FormItem>
<FormLabel>From Email *</FormLabel>
<FormControl>
<Input
placeholder={"exemple@portabase.com"} {...field} />
</FormControl>
<FormDescription>{"The email from where the email will be send"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<Button>
Save
</Button>
</Form>
</CardContent>
</Card>
</TooltipProvider>
)
}
@@ -0,0 +1,11 @@
import {z} from "zod";
export const EmailFormSchema = z.object({
smtpPassword: z.string(),
smtpFrom: z.string(),
smtpHost: z.string(),
smtpPort: z.string(),
smtpUser: z.string(),
});
export type EmailFormType = z.infer<typeof EmailFormSchema>;
@@ -0,0 +1,12 @@
import {EmailForm} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/EmailForm";
export type SettingsEmailTabProps = {}
export const SettingsEmailTab = (props:SettingsEmailTabProps) => {
return(
<div>
<EmailForm/>
</div>
)
}
@@ -0,0 +1,49 @@
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert";
import {Info} from "lucide-react";
import {Switch} from "@/components/ui/switch";
import {Label} from "@/components/ui/label";
import {StorageS3Form} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/StorageS3Form";
import {useState} from "react";
import {Settings} from "@prisma/client";
export type SettingsStorageTabProps = {
settings: Settings
}
export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
const [isSwitched, setIsSwitched] = useState<boolean>(props.settings.storage !== "local");
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"/>
<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.
</AlertDescription>
</Alert>
<div className="flex flex-col h-full py-4 ">
<div className="flex items-center space-x-2">
<Label htmlFor="storage-mode">Storage Mode (Local/s3 compatible)</Label>
<Switch
checked={isSwitched}
onCheckedChange={() => {
setIsSwitched(!isSwitched);
}}
id="storage-mode"/>
</div>
{isSwitched && (
<div className="mt-5">
<StorageS3Form/>
</div>
)}
</div>
</div>
)
}
@@ -0,0 +1,110 @@
"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 {
S3FormSchema,
S3FormType
} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/StorageS3Form/s3-form.schema";
export type S3FormProps = {
defaultValues?: S3FormType;
}
export const StorageS3Form = (props: S3FormProps) => {
const form = useZodForm({
schema: S3FormSchema,
});
const mutation = useMutation({
mutationFn: async (values: S3FormType) => {
}
})
return (
<TooltipProvider>
<Card>
<CardContent>
<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}) => (
<FormItem>
<FormLabel>Endpoint Url *</FormLabel>
<FormControl>
<Input
placeholder={"s3.eu-west-3.amazonaws.com"} {...field} />
</FormControl>
<FormDescription>{"Your s3 compatible url"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="s3AccessKeyId"
render={({field}) => (
<FormItem>
<FormLabel>Access Key *</FormLabel>
<FormControl>
<Input
placeholder={"The access key token"} {...field} />
</FormControl>
<FormDescription>{"Add your access key"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="s3SecretAccessKey"
render={({field}) => (
<FormItem>
<FormLabel>Secret Key *</FormLabel>
<FormControl>
<Input
placeholder={"The secret key token"} {...field} />
</FormControl>
<FormDescription>{"Add your secret key"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<FormField
control={form.control}
name="S3BucketName"
render={({field}) => (
<FormItem>
<FormLabel>Bucket name *</FormLabel>
<FormControl>
<Input
placeholder={"my-bucket"} {...field} />
</FormControl>
<FormDescription>{"The bucket name where you want to store your data"}</FormDescription>
<FormMessage/>
</FormItem>
)}
/>
<Button>
Save
</Button>
</Form>
</CardContent>
</Card>
</TooltipProvider>
)
}
@@ -0,0 +1,10 @@
import {z} from "zod";
export const S3FormSchema = z.object({
s3EndPointUrl: z.string(),
s3AccessKeyId: z.string(),
s3SecretAccessKey: z.string(),
S3BucketName: z.string(),
});
export type S3FormType = z.infer<typeof S3FormSchema>;
@@ -2,19 +2,19 @@
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {DataTableWithPagination} from "@/components/wrappers/table/data-table-with-pagination";
import {usersColumns} from "@/components/wrappers/Dashboard/Settings/SettingsTabs/columns-users";
import {User} from "@prisma/client";
import {User, Settings} from "@prisma/client";
import {backupColumns} from "@/features/backup/columns";
import {SettingsEmailTab} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/SettingsEmailTab";
import {SettingsStorageTab} from "@/components/wrappers/Dashboard/Settings/SettingsStorageTab/SettingsStorageTab";
export type SettingsTabsProps = {
users: User[];
settings: Settings;
}
export const SettingsTabs = (props: SettingsTabsProps) => {
return(
<Tabs defaultValue="informations" >
<TabsList className="w-full" >
@@ -24,13 +24,24 @@ export const SettingsTabs = (props: SettingsTabsProps) => {
<TabsTrigger className="w-full" value="storage">Storage</TabsTrigger>
</TabsList>
<TabsContent value="informations">
ok
<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" className="h-full justify-between">
<DataTableWithPagination columns={usersColumns} data={props.users}/>
</TabsContent>
<TabsContent value="email">Change your password here.</TabsContent>
<TabsContent value="storage">Change your password here.</TabsContent>
<TabsContent value="email">
<SettingsEmailTab/>
</TabsContent>
<TabsContent value="storage">
<SettingsStorageTab settings={props.settings}/>
</TabsContent>
</Tabs>
)
}
+41
View File
@@ -0,0 +1,41 @@
import {prisma} from "@/prisma";
export function init() {
consoleAscii()
console.log("====Init Functions====")
createSettingsIfNotExist()
.then(() => {
console.log('====Initialization completed====');
})
.catch((err) => {
console.error('Error during initialization:', err);
});
}
async function createSettingsIfNotExist() {
const settings = await prisma.settings.findUnique({
where: {
name: "system",
}
})
if(!settings){
console.log("====Init Setting====")
await prisma.settings.create({
data: {
name: "system",
}
})
}
}
function consoleAscii(){
console.log("\n" +
" ____ __ __ _____ \n" +
" / __ \\ ____ _____ / /_ ____ _ / /_ ____ _ _____ ___ / ___/ ___ _____ _ __ ___ _____\n" +
" / /_/ // __ \\ / ___// __// __ `// __ \\ / __ `// ___// _ \\ \\__ \\ / _ \\ / ___/| | / // _ \\ / ___/\n" +
" / ____// /_/ // / / /_ / /_/ // /_/ // /_/ /(__ )/ __/ ___/ // __// / | |/ // __// / \n" +
"/_/ \\____//_/ \\__/ \\__,_//_.___/ \\__,_//____/ \\___/ /____/ \\___//_/ |___/ \\___//_/ \n" +
" \n")
}