diff --git a/web/src/pages/ansible/nginx/components/hosts-field.tsx b/web/src/pages/ansible/nginx/components/hosts-field.tsx
new file mode 100644
index 00000000..33e1f399
--- /dev/null
+++ b/web/src/pages/ansible/nginx/components/hosts-field.tsx
@@ -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 (
+
+
+
+ {fields.map((_, hostIdx) => (
+
+
+ {hostIdx > 0 && (
+
+ )}
+
+ ))}
+
+
+ );
+};
+
+export default HostsField;
diff --git a/web/src/pages/ansible/nginx/nginx.tsx b/web/src/pages/ansible/nginx/nginx.tsx
index 3544aa8c..23f69fae 100644
--- a/web/src/pages/ansible/nginx/nginx.tsx
+++ b/web/src/pages/ansible/nginx/nginx.tsx
@@ -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({
+ resolver: zodResolver(nginxAnsibleSchema),
+ defaultValues,
+ });
+
+ const { mutateAsync: nginxAnsibleMutate, isPending: nginxAnsiblePending } =
+ usePost(
+ 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(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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
};
export default NginxAnsible;
diff --git a/web/src/pages/ansible/nginx/nginx.types.ts b/web/src/pages/ansible/nginx/nginx.types.ts
new file mode 100644
index 00000000..0ba1f355
--- /dev/null
+++ b/web/src/pages/ansible/nginx/nginx.types.ts
@@ -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;