mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Refactoring the sidebar.
This commit is contained in:
+34
-23
@@ -1,38 +1,49 @@
|
||||
"use client"
|
||||
|
||||
import {CreateOrganizationModal} from "@/components/wrappers/dashboard/organization/create-organisation-modal";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {authClient} from "@/lib/auth/auth-client";
|
||||
import {useState} from "react";
|
||||
import {Plus} from "lucide-react";
|
||||
|
||||
import {
|
||||
Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger
|
||||
} from "@/components/ui/dialog";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {AdminOrganizationForm} from "@/components/wrappers/dashboard/admin/organization/admin-organization-form";
|
||||
import {Plus} from "lucide-react";
|
||||
|
||||
type AdminOrganizationAddModalProps = {}
|
||||
|
||||
|
||||
export const AdminOrganizationAddModal = (props: AdminOrganizationAddModalProps) => {
|
||||
const router = useRouter();
|
||||
const {data: organizations, refetch} = authClient.useListOrganizations();
|
||||
const {data: activeOrganization, refetch: refetchActiveOrga} = authClient.useActiveOrganization();
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
if (!organizations) return null;
|
||||
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleReload = () => {
|
||||
refetch();
|
||||
refetchActiveOrga();
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
setOpenModal(true);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
<Plus/> add
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>add organization</DialogTitle>
|
||||
<DialogDescription>
|
||||
your description
|
||||
</DialogDescription>
|
||||
<AdminOrganizationForm onSuccess={() => setOpen(false)}/>
|
||||
</DialogHeader>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<>
|
||||
|
||||
<Button onClick={handleOpen}>
|
||||
<Plus/> Create a new organization
|
||||
</Button>
|
||||
<CreateOrganizationModal
|
||||
redirect={"/dashboard/admin/organizations"}
|
||||
open={openModal}
|
||||
onSuccess={handleReload}
|
||||
onOpenChange={setOpenModal}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export function organizationsListColumns(): ColumnDef<OrganizationWithMembers>[]
|
||||
<ButtonDeleteOrganization organisationId={row.original.id}/>
|
||||
)}
|
||||
<Link className={buttonVariants({variant: "outline"})}
|
||||
href={`admin/organization/${row.original.id}`}>
|
||||
href={`organizations/${row.original.id}`}>
|
||||
<Settings/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
"use server";
|
||||
import { z } from "zod";
|
||||
import { EmailFormSchema } from "@/components/wrappers/dashboard/admin/tabs/admin-email-tab/email-form/email-form.schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "@/db";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {userAction} from "@/lib/safe-actions/actions";
|
||||
|
||||
export const updateEmailSettingsAction = userAction
|
||||
.schema(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
data: EmailFormSchema,
|
||||
})
|
||||
)
|
||||
.action(async ({ parsedInput }) => {
|
||||
const { name, data } = parsedInput;
|
||||
|
||||
const [updatedSettings] = await db
|
||||
.update(drizzleDb.schemas.setting)
|
||||
.set({
|
||||
...data,
|
||||
})
|
||||
.where(eq(drizzleDb.schemas.setting.name, name))
|
||||
.returning();
|
||||
|
||||
return {
|
||||
data: updatedSettings,
|
||||
};
|
||||
});
|
||||
+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,150 @@
|
||||
"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/admin/tabs/admin-email-tab/email-form/email-form.schema";
|
||||
import {PasswordInput} from "@/components/ui/password-input";
|
||||
import {
|
||||
updateEmailSettingsAction
|
||||
} from "@/components/wrappers/dashboard/admin/tabs/admin-email-tab/email-form/email-form.action";
|
||||
import {toast} from "sonner";
|
||||
import {useRouter} from "next/navigation";
|
||||
|
||||
export type EmailFormProps = {
|
||||
defaultValues?: EmailFormType;
|
||||
};
|
||||
|
||||
export const EmailForm = (props: EmailFormProps) => {
|
||||
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) {
|
||||
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);
|
||||
}}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="smtpFrom"
|
||||
defaultValue=""
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<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>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
"use client"
|
||||
import {EmailForm} from "@/components/wrappers/dashboard/admin/tabs/admin-email-tab/email-form/email-form";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {EmailFormType} from "@/components/wrappers/dashboard/admin/tabs/admin-email-tab/email-form/email-form.schema";
|
||||
|
||||
|
||||
export type SettingsEmailSectionProps = {
|
||||
settings: Setting;
|
||||
};
|
||||
|
||||
export const SettingsEmailSection = (props: SettingsEmailSectionProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col h-full ">
|
||||
<EmailForm defaultValues={props.settings.smtpFrom ? props.settings as EmailFormType : undefined}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+62
-4
@@ -14,7 +14,7 @@ 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 {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
||||
|
||||
import {
|
||||
EmailFormSchema,
|
||||
@@ -26,17 +26,25 @@ import {
|
||||
} from "@/components/wrappers/dashboard/admin/tabs/admin-email-tab/email-form/email-form.action";
|
||||
import {toast} from "sonner";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
import {Send} from "lucide-react";
|
||||
import {sendEmail} from "@/lib/email/email-helper";
|
||||
import {render} from "@react-email/render";
|
||||
import TestEmailSettings from "@/components/emails/email-settings-test";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
export type EmailFormProps = {
|
||||
defaultValues?: EmailFormType;
|
||||
};
|
||||
|
||||
export const EmailForm = (props: EmailFormProps) => {
|
||||
const router = useRouter();
|
||||
const form = useZodForm({
|
||||
schema: EmailFormSchema,
|
||||
defaultValues: props.defaultValues,
|
||||
});
|
||||
const router = useRouter();
|
||||
const isDirty = form.formState.isDirty;
|
||||
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: async (values: EmailFormType) => {
|
||||
@@ -48,16 +56,39 @@ export const EmailForm = (props: EmailFormProps) => {
|
||||
}
|
||||
toast.success(`Success updating email informations`);
|
||||
router.refresh();
|
||||
form.reset(data)
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const mutationSendEmailTest = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!props.defaultValues?.smtpUser || !props.defaultValues?.smtpFrom) {
|
||||
toast.error("SMTP is not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
const email = await sendEmail({
|
||||
to: props.defaultValues.smtpUser,
|
||||
|
||||
subject: "Portabase",
|
||||
html: await render(TestEmailSettings(), {}),
|
||||
from: props.defaultValues.smtpFrom,
|
||||
});
|
||||
if (email.response) {
|
||||
toast.success("Test Email Successfully sent !");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Form
|
||||
form={form}
|
||||
className="flex flex-col gap-4 mt-3"
|
||||
className="flex flex-col gap-4"
|
||||
onSubmit={async (values) => {
|
||||
await mutation.mutateAsync(values);
|
||||
}}
|
||||
@@ -139,7 +170,34 @@ export const EmailForm = (props: EmailFormProps) => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end gap-4">
|
||||
<div className="flex justify-between gap-4 mt-5">
|
||||
{props.defaultValues?.smtpFrom && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
<ButtonWithLoading
|
||||
type="button"
|
||||
disabled={isDirty || mutationSendEmailTest.isPending}
|
||||
isPending={mutationSendEmailTest.isPending}
|
||||
onClick={async () => {
|
||||
await mutationSendEmailTest.mutateAsync();
|
||||
}}
|
||||
icon={<Send/>}
|
||||
size="default"
|
||||
>
|
||||
Send email test
|
||||
</ButtonWithLoading>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
|
||||
{isDirty && (
|
||||
<TooltipContent className={cn(!isDirty && "hidden")}>
|
||||
You must save changes before testing the email settings.
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<Button>Save</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user