Skip to content

Commit

Permalink
feat: create MailSettings form view with a form skeleton while loading
Browse files Browse the repository at this point in the history
  • Loading branch information
Angelillo15 committed Jul 27, 2024
1 parent b74e259 commit 81dc9c8
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 83 deletions.
6 changes: 6 additions & 0 deletions resources/scripts/api/admin/settings/getMailSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import http from '@/api/http';
import { MailSettings } from './updateMailSettings';

export default (): Promise<MailSettings> => {
return http.get('/api/application/settings/mail').then(response => response.data);
};
15 changes: 15 additions & 0 deletions resources/scripts/api/admin/settings/updateMailSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import http from '@/api/http';

export interface MailSettings {
'mail:mailers:smtp:host': string;
'mail:mailers:smtp:port': number;
'mail:mailers:smtp:encryption': string;
'mail:mailers:smtp:username': string;
'mail:mailers:smtp:password': string;
'mail:from:address': string;
'mail:from:name': string;
}

export default (settings: MailSettings): Promise<void> => {
return http.patch('/api/application/settings/mail', settings);
};
332 changes: 249 additions & 83 deletions resources/scripts/components/admin/settings/MailSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,102 +1,268 @@
import { Form, Formik } from 'formik';
import tw from 'twin.macro';
import { Form, Formik, FormikHelpers } from 'formik';

import AdminBox from '@/components/admin/AdminBox';
import Button from '@/components/elements/Button';
import Field, { FieldRow } from '@/components/elements/Field';
import Label from '@/components/elements/Label';
import Select from '@/components/elements/Select';
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Loader2 } from 'lucide-react';
import updateMailSettings, { MailSettings } from '@/api/admin/settings/updateMailSettings';
import { useEffect, useState } from 'react';
import Error, { LaravelError } from '@/components/elements/error/Error';
import { Skeleton } from '@/components/ui/skeleton';
import getMailSettings from '@/api/admin/settings/getMailSettings';
import { flushSync } from 'react-dom';

export default () => {
const submit = () => {
//
const MailSettingsForm = ({ settings }: { settings: MailSettings }) => {
const [error, setError] = useState<LaravelError | undefined>(undefined);

const submit = (settings: MailSettings, { setSubmitting }: FormikHelpers<MailSettings>) => {
setError(undefined);

updateMailSettings(settings)
.then(() => {
setSubmitting(false);
})
.catch(error => {
setError(error.response?.data);
setSubmitting(false);
});
};

return (
<Formik
onSubmit={submit}
initialValues={{
smtpHost: 'smtp.example.com',
smtpPort: 587,
smtpEncryption: 'tls',
username: '',
password: '',
mailFrom: '[email protected]',
mailFromName: 'Pterodactyl Panel',
'mail:mailers:smtp:host': settings['mail:mailers:smtp:host'],
'mail:mailers:smtp:port': settings['mail:mailers:smtp:port'],
'mail:mailers:smtp:encryption': settings['mail:mailers:smtp:encryption'],
'mail:mailers:smtp:username': settings['mail:mailers:smtp:username'],
'mail:mailers:smtp:password': '',
'mail:from:address': settings['mail:from:address'],
'mail:from:name': settings['mail:from:name'],
}}
>
{({ isSubmitting, isValid }) => (
{({ isSubmitting, isValid, values }) => (
<Form>
<AdminBox title="Mail">
<FieldRow css={tw`lg:grid-cols-3`}>
<Field
id={'smtpHost'}
name={'smtpHost'}
type={'text'}
label={'SMTP Host'}
description={''}
/>
<Field
id={'smtpPort'}
name={'smtpPort'}
type={'number'}
label={'SMTP Port'}
description={''}
/>
<div>
<Label>Encryption</Label>
<Select id={'smtpEncryption'} name={'smtpEncryption'} defaultValue={'tls'}>
<option value="">None</option>
<option value="ssl">Secure Sockets Layer (SSL)</option>
<option value="tls">Transport Layer Security (TLS)</option>
</Select>
</div>
</FieldRow>
<Card>
<CardHeader>
<CardTitle>Mail Settings</CardTitle>
<CardDescription>Configure how the Panel should send emails to users.</CardDescription>
</CardHeader>
<CardContent>
{error && <Error laravelError={error} />}

<FieldRow>
<Field
id={'username'}
name={'username'}
type={'text'}
label={'Username'}
description={''}
/>
<Field
id={'password'}
name={'password'}
type={'password'}
label={'Password'}
description={''}
/>
</FieldRow>

<FieldRow>
<Field
id={'mailFrom'}
name={'mailFrom'}
type={'text'}
label={'Mail From'}
description={''}
/>
<Field
id={'mailFromName'}
name={'mailFromName'}
type={'text'}
label={'Mail From Name'}
description={''}
/>
</FieldRow>
</AdminBox>

<div css={tw`bg-neutral-700 rounded shadow-md px-4 xl:px-5 py-4 mt-6`}>
<div css={tw`flex flex-row`}>
<Button type="submit" size="small" css={tw`ml-auto`} disabled={isSubmitting || !isValid}>
<div className="flex gap-2 lg:flex-row flex-col">
<div className="lg:w-4/6 md:3/6">
<Label htmlFor="smtpHost">SMTP Host</Label>
<Input
id="smtpHost"
name="smtpHost"
placeholder="smtp.example.com"
className="my-2"
defaultValue={values['mail:mailers:smtp:host']}
disabled={isSubmitting}
onChange={e => {
values['mail:mailers:smtp:host'] = e.target.value;
}}
/>
</div>
<div>
<Label htmlFor="smtpPort">SMTP Port</Label>
<Input
id="smtpPort"
name="smtpPort"
placeholder="587"
className="my-2"
disabled={isSubmitting}
defaultValue={values['mail:mailers:smtp:port']}
onChange={e => {
values['mail:mailers:smtp:port'] = parseInt(e.target.value);
}}
/>
</div>
<div>
<Label>SMTP Encryption</Label>
<Select
disabled={isSubmitting}
defaultValue={values['mail:mailers:smtp:encryption']}
onValueChange={value => {
values['mail:mailers:smtp:encryption'] = value;
}}
>
<SelectTrigger className="my-2">
<SelectValue placeholder="Select an encryption method" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>SMTP Encryption</SelectLabel>
<SelectItem value="tls">Transport Layer Security (TLS</SelectItem>
<SelectItem value="ssl">Secure Sockets Layer (SSL)</SelectItem>
<SelectItem value="none">None (Not recommended)</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
</div>
<div className="flex gap-2 lg:flex-row flex-col">
<div className="lg:w-2/4">
<Label htmlFor="username">SMTP Username</Label>
<Input
id="username"
name="username"
placeholder="Username"
className="my-2"
defaultValue={values['mail:mailers:smtp:username']}
disabled={isSubmitting}
onChange={e => {
values['mail:mailers:smtp:username'] = e.target.value;
}}
/>
</div>
<div className="lg:w-2/4">
<Label htmlFor="password">SMTP Password</Label>
<Input
id="password"
name="password"
type="password"
placeholder="Password"
className="my-2"
defaultValue={values['mail:mailers:smtp:password']}
disabled={isSubmitting}
onChange={e => {
values['mail:mailers:smtp:password'] = e.target.value;
}}
/>
</div>
</div>
<div className="flex gap-2 lg:flex-row flex-col">
<div className="lg:w-2/4">
<Label htmlFor="mailfrom">MailFrom</Label>
<Input
id="mailfrom"
name="mailfrom"
placeholder="[email protected]"
className="my-2"
defaultValue={values['mail:from:address']}
disabled={isSubmitting}
onChange={e => {
values['mail:from:address'] = e.target.value;
}}
/>
</div>
<div className="lg:w-2/4">
<Label htmlFor="mailFromName">Mail From Name</Label>
<Input
id="mailFromName"
name="mailFromName"
placeholder="PterodactylX panel"
className="my-2"
defaultValue={values['mail:from:name']}
disabled={isSubmitting}
onChange={e => {
values['mail:from:name'] = e.target.value;
}}
/>
</div>
</div>
</CardContent>
<CardFooter>
<Button type="submit" disabled={isSubmitting || !isValid}>
{(isSubmitting || !isValid) && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Save Changes
</Button>
</div>
</div>
</CardFooter>
</Card>
</Form>
)}
</Formik>
);
};

export default () => {
const [settings, setSettings] = useState<MailSettings | undefined>(undefined);

useEffect(() => {
const fetchSettings = async () => {
try {
const fetchedSettings = await getMailSettings();
if (document.startViewTransition) {
document.startViewTransition(() => {
flushSync(() => {
setSettings(fetchedSettings);
});
});
} else {
setSettings(fetchedSettings); // Fallback if startViewTransition is not available
}
} catch (error) {
console.error('Error fetching mail settings:', error);
}
};

fetchSettings();
}, []);

if (!settings) {
return (
<Card>
<CardHeader>
<CardTitle>Mail Settings</CardTitle>
<CardDescription>Configure how the Panel should send emails to users.</CardDescription>
</CardHeader>
<CardContent>
<div className="flex gap-2 lg:flex-row flex-col">
<div className="lg:w-4/6 md:w-3/6">
<Label htmlFor="smtpHost">SMTP Host</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
<div className="w-1/6">
<Label htmlFor="smtpPort">SMTP Port</Label>
<Skeleton className="w-full h-10 rounded-xl mt-2" />
</div>
<div className="w-1/6">
<Label>SMTP Encryption</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
</div>
<div className="flex gap-2 lg:flex-row flex-col mt-2">
<div className="lg:w-2/4">
<Label htmlFor="username">SMTP Username</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
<div className="lg:w-2/4">
<Label htmlFor="password">SMTP Password</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
</div>
<div className="flex gap-2 lg:flex-row flex-col mt-2">
<div className="lg:w-2/4">
<Label htmlFor="mailfrom">MailFrom</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
<div className="lg:w-2/4">
<Label htmlFor="mailFromName">Mail From Name</Label>
<Skeleton className="w-full rounded-xl h-10 mt-2" />
</div>
</div>
</CardContent>
<CardFooter>
<Button disabled className="mt-2">
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading Settings
</Button>
</CardFooter>
</Card>
);
} else {
return <MailSettingsForm settings={settings} />;
}
};

0 comments on commit 81dc9c8

Please sign in to comment.