mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # package.json
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
"use client"
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {useState} from "react";
|
||||
import {Loader2} from "lucide-react";
|
||||
|
||||
export type VariantButton = {
|
||||
secondary: string
|
||||
default: string
|
||||
outline: string
|
||||
ghost: string
|
||||
link: string
|
||||
destructive: string
|
||||
}
|
||||
|
||||
export type ButtonWithConfirmProps = {
|
||||
icon?: any,
|
||||
text: string,
|
||||
variant?: keyof VariantButton ,
|
||||
className?: string,
|
||||
onClick: () => void,
|
||||
isPending? : boolean
|
||||
};
|
||||
|
||||
export const ButtonWithLoading = (props: ButtonWithConfirmProps) => {
|
||||
return(
|
||||
<Button
|
||||
onClick={() => {
|
||||
props.onClick()
|
||||
}}
|
||||
variant={props.variant ? props.variant : "default"}
|
||||
className={props.className}
|
||||
>
|
||||
{props.isPending && <Loader2 className="animate-spin mr-4" size={16}/>}
|
||||
{props.text}
|
||||
<>
|
||||
{props.icon ? props.icon : null}
|
||||
</>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
+101
-4
@@ -14,26 +14,50 @@ import {
|
||||
EmailFormSchema,
|
||||
EmailFormType
|
||||
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
|
||||
import Link from "next/link";
|
||||
import {PasswordInput} from "@/components/wrappers/Auth/PaswordInput/password-input";
|
||||
import {
|
||||
updateEmailSettingsAction
|
||||
} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/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,
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (values: EmailFormType) => {
|
||||
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Card>
|
||||
<CardContent>
|
||||
|
||||
<Form form={form}
|
||||
|
||||
className="flex flex-col gap-4 mt-3"
|
||||
onSubmit={async (values) => {
|
||||
await mutation.mutateAsync(values);
|
||||
@@ -42,6 +66,8 @@ export const EmailForm = (props: EmailFormProps) => {
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpFrom"
|
||||
defaultValue=""
|
||||
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>From Email *</FormLabel>
|
||||
@@ -54,10 +80,81 @@ export const EmailForm = (props: EmailFormProps) => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpHost"
|
||||
defaultValue=""
|
||||
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Server Host *</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={"ssl0.ovh.net"} {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>{"Your email server host"}</FormDescription>
|
||||
<FormMessage/>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpPort"
|
||||
defaultValue=""
|
||||
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Server Port *</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={"465"} {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>{"Your email server port (send)"}</FormDescription>
|
||||
<FormMessage/>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpPassword"
|
||||
defaultValue=""
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<PasswordInput placeholder="Password" {...field}/>
|
||||
</FormControl>
|
||||
<FormDescription>{"Your email server password"}</FormDescription>
|
||||
|
||||
<FormMessage/>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpUser"
|
||||
defaultValue=""
|
||||
|
||||
render={({field}) => (
|
||||
<FormItem>
|
||||
<FormLabel>User Email *</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={"exemple@portabase.com"} {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>{"The email server user"}</FormDescription>
|
||||
<FormMessage/>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end gap-4">
|
||||
|
||||
<Button>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button>
|
||||
Save
|
||||
</Button>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
"use server"
|
||||
import {userAction} from "@/safe-actions";
|
||||
import {z} from "zod";
|
||||
import {prisma} from "@/prisma";
|
||||
import {EmailFormSchema} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/email-form.schema";
|
||||
|
||||
|
||||
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,
|
||||
})
|
||||
|
||||
return {
|
||||
data: updatedSettings,
|
||||
|
||||
}
|
||||
})
|
||||
@@ -1,12 +1,60 @@
|
||||
import {EmailForm} from "@/components/wrappers/Dashboard/Settings/SettingsEmailTab/EmailForm/EmailForm";
|
||||
import {Settings} from "@prisma/client";
|
||||
import {Send} from "lucide-react";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/Button/ButtonWithLoading/ButtonWithLoading";
|
||||
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";
|
||||
|
||||
|
||||
export type SettingsEmailTabProps = {}
|
||||
|
||||
export const SettingsEmailTab = (props:SettingsEmailTabProps) => {
|
||||
return(
|
||||
<div>
|
||||
<EmailForm/>
|
||||
export type SettingsEmailTabProps = {
|
||||
settings: Settings
|
||||
}
|
||||
|
||||
export const SettingsEmailTab = (props: SettingsEmailTabProps) => {
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const email = await sendEmail({
|
||||
to: props.settings.smtpUser,
|
||||
subject: "Portabase email test !",
|
||||
html: await render(TestEmailSettings(), {})
|
||||
});
|
||||
if(email.response){
|
||||
toast.success("Test Email Successfully sent !");
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
const handleSendMailTest = async () => {
|
||||
console.log("Sending email test...");
|
||||
await mutation.mutateAsync()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full py-4">
|
||||
<div className="flex gap-4 h-fit justify-between">
|
||||
<h1>Settings for Portabase storage</h1>
|
||||
{props.settings.smtpFrom && (
|
||||
<ButtonWithLoading
|
||||
isPending={mutation.isPending}
|
||||
onClick={async () => {
|
||||
await handleSendMailTest()
|
||||
}}
|
||||
icon={<Send/>}
|
||||
text="Send email test"
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<EmailForm defaultValues={props.settings.smtpFrom ? props.settings : null}/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ export const SettingsStorageTab = (props: SettingsStorageTabProps) => {
|
||||
</div>
|
||||
{isSwitched && (
|
||||
<div className="mt-5">
|
||||
<StorageS3Form/>
|
||||
<StorageS3Form defaultValues={props.settings.s3EndPointUrl ? props.settings : null}/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+7
-3
@@ -22,6 +22,7 @@ export type S3FormProps = {
|
||||
export const StorageS3Form = (props: S3FormProps) => {
|
||||
const form = useZodForm({
|
||||
schema: S3FormSchema,
|
||||
defaultValues: props.defaultValues,
|
||||
});
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (values: S3FormType) => {
|
||||
@@ -99,9 +100,12 @@ export const StorageS3Form = (props: S3FormProps) => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button>
|
||||
Save
|
||||
</Button>
|
||||
<div className="flex justify-end gap-4">
|
||||
|
||||
<Button>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const SettingsTabs = (props: SettingsTabsProps) => {
|
||||
<DataTableWithPagination columns={usersColumns} data={props.users}/>
|
||||
</TabsContent>
|
||||
<TabsContent value="email">
|
||||
<SettingsEmailTab/>
|
||||
<SettingsEmailTab settings={props.settings}/>
|
||||
</TabsContent>
|
||||
<TabsContent value="storage">
|
||||
<SettingsStorageTab settings={props.settings}/>
|
||||
|
||||
@@ -15,11 +15,11 @@ export const SidebarMenuCustom = (props: SidebarMenuCustomProps) => {
|
||||
const pathname = usePathname();
|
||||
// Menu items.
|
||||
const items = [
|
||||
{
|
||||
title: "Home",
|
||||
url: "/",
|
||||
icon: Home,
|
||||
},
|
||||
// {
|
||||
// title: "Home",
|
||||
// url: "/",
|
||||
// icon: Home,
|
||||
// },
|
||||
{
|
||||
title: "Agents",
|
||||
url: "agents",
|
||||
|
||||
Reference in New Issue
Block a user