mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Working on settings Page. Adding init function on next.js startup.
This commit is contained in:
+10
-7
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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");
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
+11
@@ -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>
|
||||
)
|
||||
}
|
||||
+110
@@ -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>
|
||||
)
|
||||
}
|
||||
+10
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user