Skip to content

Commit

Permalink
feat(web): Add download functionality to Ansible Template Nginx service
Browse files Browse the repository at this point in the history
  • Loading branch information
MiladSadeghi committed Dec 4, 2024
1 parent 946da72 commit 79d52ab
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 1 deletion.
46 changes: 46 additions & 0 deletions web/src/pages/ansible/nginx/components/hosts-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { FormInput } from '@/components/form/form-input';
import { Plus, Trash2 } from 'lucide-react';
import { FC } from 'react';
import { useFieldArray, useFormContext } from 'react-hook-form';

const HostsField: FC = () => {
const { control } = useFormContext();

const { fields, append, remove } = useFieldArray({
control,
name: 'hosts',
});

return (
<div>
<div className="flex items-center mb-2">
<p className="text-lg font-bold">Hosts</p>
<button type="button" onClick={append} className="ml-4 btn btn-xs">
Add <Plus className="size-3" />
</button>
</div>
<div className="space-y-2">
{fields.map((_, hostIdx) => (
<div className="relative" key={hostIdx}>
<FormInput
id={`hosts_input.${hostIdx}`}
name={`hosts.${hostIdx}.value`}
label=""
placeholder="www.example.com"
/>
{hostIdx > 0 && (
<button
onClick={() => remove(hostIdx)}
className="absolute right-3 top-3"
>
<Trash2 className="size-4" />
</button>
)}
</div>
))}
</div>
</div>
);
};

export default HostsField;
120 changes: 119 additions & 1 deletion web/src/pages/ansible/nginx/nginx.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,125 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { FC } from 'react';
import { useForm } from 'react-hook-form';
import {
NginxAnsibleBody,
NginxAnsibleResponse,
nginxAnsibleSchema,
nginxTemplateValidationError,
} from './nginx.types';
import { AnsibleTemplateAPI } from '@/enums/api.enums';
import { usePost } from '@/core/react-query';
import { useDownload } from '@/hooks';
import { isAxiosError } from 'axios';
import { toast } from 'sonner';
import { FormWrapper } from '@/components/form/form-wrapper';
import { FormInput } from '@/components/form/form-input';
import { FormSelect } from '@/components/form/form-select';
import HostsField from './components/hosts-field';
import { OSOptions } from './data/select-options';
import type { NginxAnsible } from './nginx.types';

const NginxAnsible: FC = () => {
return <>Nginx</>;
const defaultValues = {
ansible_user: '',
os: { label: 'Ubuntu', value: 'ubuntu' },
hosts: [{ value: '' }],
version: '',
};

const methods = useForm<NginxAnsible>({
resolver: zodResolver(nginxAnsibleSchema),
defaultValues,
});

const { mutateAsync: nginxAnsibleMutate, isPending: nginxAnsiblePending } =
usePost<NginxAnsibleResponse, NginxAnsibleBody>(
AnsibleTemplateAPI.Nginx,
'ansible-nginx',
);

const { download, isPending: downloadPending } = useDownload({
downloadFileName: 'NginxAnsible',
source: 'nginx',
folderName: 'MyAnsible',
});

const handleSubmit = async (data: NginxAnsible) => {
try {
const body = {
...data,
hosts: data.hosts.map((host) => host.value),
os: data.os.value,
};

await nginxAnsibleMutate(body);
await download();
} catch (error) {
console.log(error);
if (isAxiosError<nginxTemplateValidationError>(error)) {
toast.error(
`${error.response?.data.detail[0].loc[error.response?.data.detail[0].loc.length - 1]} ${error.response?.data.detail[0].msg}`,
);
} else {
toast.error('Something went wrong');
}
}
};

return (
<div className="w-full text-black max-w-96 dark:text-white">
<FormWrapper methods={methods} onSubmit={handleSubmit}>
<div className="mb-4">
<FormInput
id="ansible_user"
name={`ansible_user`}
label="User"
placeholder="root"
/>
</div>
<div className="mb-4">
<FormInput
id="ansible_port"
name={`ansible_port`}
label="Port"
placeholder="22"
inputType={'number'}
isNumber={true}
/>
</div>
<div className="mb-4">
<FormSelect
name={`os`}
label="OS"
placeholder="Select..."
options={OSOptions}
/>
</div>
<div className="mb-4">
<HostsField />
</div>
<div className="mb-4">
<FormInput
id="version"
name={`version`}
label="Version"
placeholder="3.11"
/>
</div>
<button
type="submit"
disabled={nginxAnsiblePending}
className="w-full mt-3 text-white btn bg-orange-base hover:bg-orange-base/70 disabled:bg-orange-base/50 disabled:text-white/70"
>
{nginxAnsiblePending
? 'Generating...'
: downloadPending
? 'Downloading...'
: 'Generate'}
</button>
</FormWrapper>
</div>
);
};

export default NginxAnsible;
45 changes: 45 additions & 0 deletions web/src/pages/ansible/nginx/nginx.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { z as zod } from 'zod';

export interface NginxAnsibleResponse {
output: string;
}

export interface NginxAnsibleBody {
ansible_user: string;
ansible_port: number;
os: string;
hosts: string[];
version: string;
}

export interface nginxTemplateValidationError {
detail: [
{
type: string;
loc: string[];
msg: string;
input: null;
},
];
}

export const nginxAnsibleSchema = zod.object({
ansible_user: zod.string().min(1, 'User is required!'),
ansible_port: zod
.number({ invalid_type_error: 'Port is required!' })
.min(1, 'Port is required!'),
os: zod.object({
label: zod.string(),
value: zod.string(),
}),
hosts: zod
.array(
zod.object({
value: zod.string().min(1, 'Host is required!'),
}),
)
.min(1),
version: zod.string().min(1, 'Version is required!'),
});

export type NginxAnsible = zod.infer<typeof nginxAnsibleSchema>;

0 comments on commit 79d52ab

Please sign in to comment.