Refactoring the sidebar.

This commit is contained in:
charlesgauthereau
2025-12-06 15:50:23 +01:00
parent 5cf6cf075e
commit 9458b12d69
23 changed files with 665 additions and 226 deletions
@@ -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>
@@ -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,
};
});
@@ -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>
);
};
@@ -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>