Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
725daea
feat(web): Add Ansible Template route with placeholder Services page
MiladSadeghi Dec 4, 2024
cb19b79
feat(web): Add download functionality to Ansible Template Docker service
MiladSadeghi Dec 4, 2024
2cb9318
refactor(web): Require all fields in Ansible Template Docker service …
MiladSadeghi Dec 4, 2024
946da72
feat(web): Add download functionality to Ansible Template Kubernetes …
MiladSadeghi Dec 4, 2024
79d52ab
feat(web): Add download functionality to Ansible Template Nginx service
MiladSadeghi Dec 4, 2024
76bbb72
refactor(web): Fix Helm Template structure and styling issues
MiladSadeghi Dec 4, 2024
f6e264f
chore(web): Add missing data folders
MiladSadeghi Dec 4, 2024
b9f0eb4
refactor(web): Update `version` input to dropdown in Ansible Template…
MiladSadeghi Dec 4, 2024
c30f0ee
refactor(web): Update `version` input to dropdown in Ansible Template…
MiladSadeghi Dec 4, 2024
97299e4
refactor(web): Update environment field border color for error state …
MiladSadeghi Dec 4, 2024
38a4cda
refactor(web): Remove LB nodes from kuber service in Ansible template
MiladSadeghi Dec 4, 2024
1e6ef77
refactor(web): Reorder navbar links
MiladSadeghi Dec 4, 2024
644d7e4
chore(web): Rename argocd to `ArgoCD` across the application
MiladSadeghi Dec 4, 2024
1d04c54
style(web): Increase height of bug fix and basic page message box
MiladSadeghi Dec 4, 2024
3b9d06e
feat(web): Add new Nginx version options in Ansible Template service
MiladSadeghi Dec 4, 2024
b4745fe
feat(web): Add version combobox for docker service in Ansible Template
MiladSadeghi Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ import {
Installation,
S3,
} from '@/pages';
import { AnsibleLayout } from './pages/ansible/components/layout';
import DockerAnsible from './pages/ansible/docker/docker';
import NginxAnsible from './pages/ansible/nginx/nginx';
import KubernetesAnsible from './pages/ansible/kuber/kuber';

function App() {
const location = useLocation();
return (
<div>
<div className="container mx-auto border-l border-r border-gray-700 h-dvh max-w-7xl">
<div className="container mx-auto h-dvh max-w-7xl border-l border-r border-gray-700">
<Routes location={location}>
<Route element={<MainLayout />}>
<Route index element={<Basic />} />
Expand All @@ -30,6 +34,11 @@ function App() {
<Route path="iam" element={<IAM />} />
<Route path="argocd" element={<Argocd />} />
</Route>
<Route path="ansible-template" element={<AnsibleLayout />}>
<Route path="docker" element={<DockerAnsible />} />
<Route path="nginx" element={<NginxAnsible />} />
<Route path="kuber" element={<KubernetesAnsible />} />
</Route>
<Route path="installation" element={<Installation />} />
</Route>
</Routes>
Expand Down
27 changes: 21 additions & 6 deletions web/src/components/form/form-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { FormFieldProps } from '../../types/form.types';
import { getNestedValue } from '@/lib/helper';
import { cn } from '@/lib/utils';

export const FormInput = ({ name, label, error, ...props }: FormFieldProps) => {
export const FormInput = ({
name,
label,
error,
isNumber,
inputType,
inputClass,
...props
}: FormFieldProps) => {
const {
register,
formState: { errors },
Expand All @@ -21,20 +29,27 @@ export const FormInput = ({ name, label, error, ...props }: FormFieldProps) => {
name={name}
>
{label && (
<div className="mb-2 flex items-baseline justify-between">
<Form.Label className="form-label">{label} :</Form.Label>
<div className="flex items-baseline justify-between mb-1">
<Form.Label className="form-label">{label}</Form.Label>
</div>
)}
<Form.Control asChild>
<input
className="w-full rounded-md border border-gray-200 px-3 py-2 outline-none dark:border-none"
{...register(name)}
type={inputType}
className={cn(
'w-full rounded-md border border-gray-500 px-3 py-2 outline-none transition-all focus:border-orange-base',
props.className,
{
'border-red-500 dark:border': errorMessage,
},
)}
{...register(name, { ...(isNumber && { valueAsNumber: true }) })}
{...props}
/>
</Form.Control>
{errorMessage && (
<div className="absolute left-0 top-full">
<Form.Message className="form-message ml-auto text-sm text-red-500">
<Form.Message className="ml-auto text-sm text-red-500 form-message">
{errorMessage}
</Form.Message>
</div>
Expand Down
8 changes: 4 additions & 4 deletions web/src/components/form/form-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Controller, useFormContext } from 'react-hook-form';
import { FormFieldProps } from '../../types/form.types';
import Select from 'react-select';
import { getNestedValue } from '@/lib/helper';
import { selectStyle } from '@/pages/helm-template/styles/helm-template.style';
import { useStyle } from '@/hooks';
import { cn } from '@/lib/utils';
import { selectStyle } from '@/styles/select.styles';

interface OptionType {
value: string;
Expand Down Expand Up @@ -46,8 +46,8 @@ export const FormSelect = ({
name={name}
>
{label && (
<div className="mb-2 flex items-baseline justify-between">
<Form.Label className="form-label">{label} :</Form.Label>
<div className="mb-1 flex items-baseline justify-between">
<Form.Label className="form-label">{label}</Form.Label>
</div>
)}
<Form.Control asChild>
Expand All @@ -61,7 +61,7 @@ export const FormSelect = ({
placeholder={placeholder}
className="w-full"
{...props}
styles={selectStyle(darkMode)}
styles={selectStyle(darkMode, !!errorMessage)}
/>
)}
/>
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/form/form-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const FormWrapper = <T extends z.ZodType>({
methods,
}: FormWrapperProps<T>) => {
return (
<FormProvider {...methods} >
<FormProvider {...methods}>
<Form.Root
onSubmit={methods.handleSubmit(onSubmit)}
className="text-black dark:text-white"
Expand Down
12 changes: 8 additions & 4 deletions web/src/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@ const navbar = [
url: '/bug-fix',
title: 'Bug Fix',
},
{
url: '/terraform-template',
title: 'Terraform Template',
},
{
url: '/installation',
title: 'Installation',
},
{
url: '/terraform-template',
title: 'Terraform Template',
},
{
url: '/helm-template',
title: 'Helm Template',
},
{
url: '/ansible-template',
title: 'Ansible Template',
},
];

const Navbar: FC = () => {
Expand Down
6 changes: 6 additions & 0 deletions web/src/enums/api.enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ export enum API {
Installation = '/IaC-install',
HelmTemplate = '/Helm-template',
}

export enum AnsibleTemplateAPI {
Docker = '/ansible-install/docker',
Nginx = '/ansible-install/nginx',
Kubernetes = '/ansible-install/kuber',
}
40 changes: 40 additions & 0 deletions web/src/pages/ansible/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { FC } from 'react';
import { NavLink, Outlet } from 'react-router';

const menu = [
{
url: 'nginx',
title: 'Nginx Service',
},
{
url: 'docker',
title: 'Docker Service',
},
{
url: 'kuber',
title: 'Kubernetes Service',
},
];

export const AnsibleLayout: FC = () => {
return (
<div className="flex h-[calc(100vh-56px)] items-center">
<div className="flex h-full w-full max-w-96 flex-col items-center justify-center divide-y divide-gray-500 border-r border-gray-500">
{menu.map((link) => (
<NavLink
key={link.url}
to={link.url}
className={({ isActive }) =>
`block w-full p-4 text-center text-black outline-none transition-all dark:text-white ${isActive ? 'bg-orange-base text-white' : ''}`
}
>
{link.title}
</NavLink>
))}
</div>
<div className="flex h-full w-2/3 items-center justify-center">
<Outlet />
</div>
</div>
);
};
46 changes: 46 additions & 0 deletions web/src/pages/ansible/docker/components/hosts-fields.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;
13 changes: 13 additions & 0 deletions web/src/pages/ansible/docker/data/select-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const OSOptions = [
{
value: 'ubuntu',
label: 'Ubuntu',
},
];

export const versionOptions = [
{
label: 'Latest',
value: 'latest',
},
];
129 changes: 129 additions & 0 deletions web/src/pages/ansible/docker/docker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { FC } from 'react';
import {
dockerAnsibleSchema,
type DockerAnsible,
type DockerAnsibleBody,
type DockerAnsibleResponse,
type dockerTemplateValidationError,
} from './docker.types';
import { AnsibleTemplateAPI } from '@/enums/api.enums';
import { useDownload } from '@/hooks';
import { usePost } from '@/core/react-query';
import { isAxiosError } from 'axios';
import { toast } from 'sonner';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { FormWrapper } from '@/components/form/form-wrapper';
import { FormInput } from '@/components/form/form-input';
import HostsField from './components/hosts-fields';
import { FormSelect } from '@/components/form/form-select';
import { OSOptions, versionOptions } from './data/select-options';

const DockerAnsible: FC = () => {
const defaultValues = {
ansible_user: '',
os: { label: 'Ubuntu', value: 'ubuntu' },
hosts: [{ value: '' }],
version: {
label: 'Latest',
value: 'latest',
},
};

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

const { mutateAsync: dockerAnsibleMutate, isPending: dockerAnsiblePending } =
usePost<DockerAnsibleResponse, DockerAnsibleBody>(
AnsibleTemplateAPI.Docker,
'ansible-docker',
);

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

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

await dockerAnsibleMutate(body);
await download();
} catch (error) {
console.log(error);
if (isAxiosError<dockerTemplateValidationError>(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 max-w-96 text-black 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">
<FormSelect
name={`version`}
label="Version"
placeholder="Select..."
options={versionOptions}
/>
</div>
<button
type="submit"
disabled={dockerAnsiblePending}
className="btn mt-3 w-full bg-orange-base text-white hover:bg-orange-base/70 disabled:bg-orange-base/50 disabled:text-white/70"
>
{dockerAnsiblePending
? 'Generating...'
: downloadPending
? 'Downloading...'
: 'Generate'}
</button>
</FormWrapper>
</div>
);
};

export default DockerAnsible;
Loading
Loading