}>
} />
@@ -30,6 +34,11 @@ function App() {
} />
} />
+ }>
+ } />
+ } />
+ } />
+
} />
diff --git a/web/src/components/form/form-input.tsx b/web/src/components/form/form-input.tsx
index 50eb4c90..be46e833 100644
--- a/web/src/components/form/form-input.tsx
+++ b/web/src/components/form/form-input.tsx
@@ -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 },
@@ -21,20 +29,27 @@ export const FormInput = ({ name, label, error, ...props }: FormFieldProps) => {
name={name}
>
{label && (
-
-
{label} :
+
+
{label}
)}
{errorMessage && (
-
+
{errorMessage}
diff --git a/web/src/components/form/form-select.tsx b/web/src/components/form/form-select.tsx
index 25b0b513..e7591938 100644
--- a/web/src/components/form/form-select.tsx
+++ b/web/src/components/form/form-select.tsx
@@ -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;
@@ -46,8 +46,8 @@ export const FormSelect = ({
name={name}
>
{label && (
-
-
{label} :
+
+
{label}
)}
@@ -61,7 +61,7 @@ export const FormSelect = ({
placeholder={placeholder}
className="w-full"
{...props}
- styles={selectStyle(darkMode)}
+ styles={selectStyle(darkMode, !!errorMessage)}
/>
)}
/>
diff --git a/web/src/components/form/form-wrapper.tsx b/web/src/components/form/form-wrapper.tsx
index d3e92c95..a37eb225 100644
--- a/web/src/components/form/form-wrapper.tsx
+++ b/web/src/components/form/form-wrapper.tsx
@@ -16,7 +16,7 @@ export const FormWrapper = ({
methods,
}: FormWrapperProps) => {
return (
-
+
{
diff --git a/web/src/enums/api.enums.ts b/web/src/enums/api.enums.ts
index 029e3c7b..1d8f7fb6 100644
--- a/web/src/enums/api.enums.ts
+++ b/web/src/enums/api.enums.ts
@@ -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',
+}
diff --git a/web/src/pages/ansible/components/layout.tsx b/web/src/pages/ansible/components/layout.tsx
new file mode 100644
index 00000000..d7f1abc7
--- /dev/null
+++ b/web/src/pages/ansible/components/layout.tsx
@@ -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 (
+
+
+ {menu.map((link) => (
+
+ `block w-full p-4 text-center text-black outline-none transition-all dark:text-white ${isActive ? 'bg-orange-base text-white' : ''}`
+ }
+ >
+ {link.title}
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/web/src/pages/ansible/docker/components/hosts-fields.tsx b/web/src/pages/ansible/docker/components/hosts-fields.tsx
new file mode 100644
index 00000000..33e1f399
--- /dev/null
+++ b/web/src/pages/ansible/docker/components/hosts-fields.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/docker/data/select-options.ts b/web/src/pages/ansible/docker/data/select-options.ts
new file mode 100644
index 00000000..f87f0f4a
--- /dev/null
+++ b/web/src/pages/ansible/docker/data/select-options.ts
@@ -0,0 +1,13 @@
+export const OSOptions = [
+ {
+ value: 'ubuntu',
+ label: 'Ubuntu',
+ },
+];
+
+export const versionOptions = [
+ {
+ label: 'Latest',
+ value: 'latest',
+ },
+];
diff --git a/web/src/pages/ansible/docker/docker.tsx b/web/src/pages/ansible/docker/docker.tsx
new file mode 100644
index 00000000..d4617770
--- /dev/null
+++ b/web/src/pages/ansible/docker/docker.tsx
@@ -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({
+ resolver: zodResolver(dockerAnsibleSchema),
+ defaultValues,
+ });
+
+ const { mutateAsync: dockerAnsibleMutate, isPending: dockerAnsiblePending } =
+ usePost(
+ 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(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 DockerAnsible;
diff --git a/web/src/pages/ansible/docker/docker.types.ts b/web/src/pages/ansible/docker/docker.types.ts
new file mode 100644
index 00000000..b4a2a5f2
--- /dev/null
+++ b/web/src/pages/ansible/docker/docker.types.ts
@@ -0,0 +1,48 @@
+import { z as zod } from 'zod';
+
+export interface DockerAnsibleResponse {
+ output: string;
+}
+
+export interface DockerAnsibleBody {
+ ansible_user: string;
+ ansible_port: number;
+ os: string;
+ hosts: string[];
+ version: string;
+}
+
+export interface dockerTemplateValidationError {
+ detail: [
+ {
+ type: string;
+ loc: string[];
+ msg: string;
+ input: null;
+ },
+ ];
+}
+
+export const dockerAnsibleSchema = 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.object({
+ label: zod.string(),
+ value: zod.string(),
+ }),
+});
+
+export type DockerAnsible = zod.infer;
diff --git a/web/src/pages/ansible/kuber/components/k8s-master-nodes.tsx b/web/src/pages/ansible/kuber/components/k8s-master-nodes.tsx
new file mode 100644
index 00000000..2ac9bf28
--- /dev/null
+++ b/web/src/pages/ansible/kuber/components/k8s-master-nodes.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 K8SMasterNodes: FC = () => {
+ const { control } = useFormContext();
+
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: 'k8s_master_nodes',
+ });
+
+ return (
+
+
+
K8s Master Nodes
+
+
+
+ {fields.map((_, nodeIdx) => (
+
+
+ {nodeIdx > 0 && (
+
+ )}
+
+ ))}
+
+
+ );
+};
+
+export default K8SMasterNodes;
diff --git a/web/src/pages/ansible/kuber/components/k8s-worker-nodes.tsx b/web/src/pages/ansible/kuber/components/k8s-worker-nodes.tsx
new file mode 100644
index 00000000..3a7ee574
--- /dev/null
+++ b/web/src/pages/ansible/kuber/components/k8s-worker-nodes.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 K8SWorkerNodes: FC = () => {
+ const { control } = useFormContext();
+
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: 'k8s_worker_nodes',
+ });
+
+ return (
+
+
+
K8s Worker Nodes
+
+
+
+ {fields.map((_, nodeIdx) => (
+
+
+ {nodeIdx > 0 && (
+
+ )}
+
+ ))}
+
+
+ );
+};
+
+export default K8SWorkerNodes;
diff --git a/web/src/pages/ansible/kuber/data/select-options.ts b/web/src/pages/ansible/kuber/data/select-options.ts
new file mode 100644
index 00000000..01c1cf70
--- /dev/null
+++ b/web/src/pages/ansible/kuber/data/select-options.ts
@@ -0,0 +1,21 @@
+export const OSOptions = [
+ {
+ value: 'ubuntu',
+ label: 'Ubuntu',
+ },
+];
+
+export const versionOptions = [
+ {
+ label: '1.31',
+ value: '1.31',
+ },
+ {
+ label: '1.30',
+ value: '1.30',
+ },
+ {
+ label: '1.29',
+ value: '1.29',
+ },
+];
diff --git a/web/src/pages/ansible/kuber/kuber.tsx b/web/src/pages/ansible/kuber/kuber.tsx
new file mode 100644
index 00000000..b76a06a8
--- /dev/null
+++ b/web/src/pages/ansible/kuber/kuber.tsx
@@ -0,0 +1,131 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import { FC } from 'react';
+import { useForm } from 'react-hook-form';
+import {
+ KuberAnsible,
+ KuberAnsibleBody,
+ KuberAnsibleResponse,
+ kuberAnsibleSchema,
+ kuberTemplateValidationError,
+} from './kuber.types';
+import { FormWrapper } from '@/components/form/form-wrapper';
+import { FormInput } from '@/components/form/form-input';
+import { FormSelect } from '@/components/form/form-select';
+import { usePost } from '@/core/react-query';
+import { AnsibleTemplateAPI } from '@/enums/api.enums';
+import { OSOptions, versionOptions } from './data/select-options';
+import { useDownload } from '@/hooks';
+import { isAxiosError } from 'axios';
+import { toast } from 'sonner';
+import K8SMasterNodes from './components/k8s-master-nodes';
+import K8SWorkerNodes from './components/k8s-worker-nodes';
+
+const KubernetesAnsible: FC = () => {
+ const { mutateAsync: kuberAnsibleMutate, isPending: kuberAnsiblePending } =
+ usePost(
+ AnsibleTemplateAPI.Kubernetes,
+ 'ansible-docker',
+ );
+
+ const { download, isPending: downloadPending } = useDownload({
+ downloadFileName: 'KubernetesAnsible',
+ source: 'kubernetes',
+ folderName: 'MyAnsible',
+ });
+
+ const defaultValues = {
+ ansible_user: '',
+ os: { label: 'Ubuntu', value: 'ubuntu' },
+ k8s_worker_nodes: [{ value: '' }],
+ k8s_master_nodes: [{ value: '' }],
+ };
+
+ const methods = useForm({
+ resolver: zodResolver(kuberAnsibleSchema),
+ defaultValues,
+ });
+
+ const handleSubmit = async (data: KuberAnsible) => {
+ try {
+ const body = {
+ ...data,
+ k8s_worker_nodes: data.k8s_worker_nodes.map((worker) => worker.value),
+ k8s_master_nodes: data.k8s_master_nodes.map((master) => master.value),
+ os: data.os.value,
+ version: data.version.value,
+ };
+
+ await kuberAnsibleMutate(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 KubernetesAnsible;
diff --git a/web/src/pages/ansible/kuber/kuber.types.ts b/web/src/pages/ansible/kuber/kuber.types.ts
new file mode 100644
index 00000000..25bd1f76
--- /dev/null
+++ b/web/src/pages/ansible/kuber/kuber.types.ts
@@ -0,0 +1,54 @@
+import { z as zod } from 'zod';
+
+export interface KuberAnsibleResponse {
+ output: string;
+}
+
+export interface KuberAnsibleBody {
+ ansible_user: string;
+ ansible_port: number;
+ os: string;
+ k8s_worker_nodes: string[];
+ k8s_master_nodes: string[];
+ version: string;
+}
+
+export interface kuberTemplateValidationError {
+ detail: [
+ {
+ type: string;
+ loc: string[];
+ msg: string;
+ input: null;
+ },
+ ];
+}
+
+export const kuberAnsibleSchema = 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(),
+ }),
+ k8s_worker_nodes: zod
+ .array(
+ zod.object({
+ value: zod.string().min(1, 'Required!').default('www.example.com'),
+ }),
+ )
+ .min(1),
+ k8s_master_nodes: zod.array(
+ zod.object({
+ value: zod.string().min(1, 'Required!').default('www.example.com'),
+ }),
+ ),
+ version: zod.object({
+ label: zod.string().min(1, 'Required!'),
+ value: zod.string().min(1, 'Required!'),
+ }),
+});
+
+export type KuberAnsible = zod.infer;
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/data/select-options.ts b/web/src/pages/ansible/nginx/data/select-options.ts
new file mode 100644
index 00000000..fdde30b8
--- /dev/null
+++ b/web/src/pages/ansible/nginx/data/select-options.ts
@@ -0,0 +1,29 @@
+export const OSOptions = [
+ {
+ value: 'ubuntu',
+ label: 'Ubuntu',
+ },
+];
+
+export const VersionOptions = [
+ {
+ value: 'latest',
+ label: 'Latest',
+ },
+ {
+ value: '1.27.3-1',
+ label: '1.27.3-1',
+ },
+ {
+ value: '1.25.5-1',
+ label: '1.25.5-1',
+ },
+ {
+ value: '1.23.4-1',
+ label: '1.23.4-1',
+ },
+ {
+ value: '1.21.6-1',
+ label: '1.21.6-1',
+ },
+];
diff --git a/web/src/pages/ansible/nginx/nginx.tsx b/web/src/pages/ansible/nginx/nginx.tsx
new file mode 100644
index 00000000..0affb759
--- /dev/null
+++ b/web/src/pages/ansible/nginx/nginx.tsx
@@ -0,0 +1,129 @@
+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, VersionOptions } from './data/select-options';
+import type { NginxAnsible } from './nginx.types';
+
+const NginxAnsible: FC = () => {
+ const defaultValues = {
+ ansible_user: '',
+ os: { label: 'Ubuntu', value: 'ubuntu' },
+ hosts: [{ value: '' }],
+ version: {
+ label: 'Latest',
+ value: 'latest',
+ },
+ };
+
+ 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,
+ version: data.version.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..77324d00
--- /dev/null
+++ b/web/src/pages/ansible/nginx/nginx.types.ts
@@ -0,0 +1,48 @@
+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.object({
+ label: zod.string(),
+ value: zod.string(),
+ }),
+});
+
+export type NginxAnsible = zod.infer;
diff --git a/web/src/pages/basic/basic.tsx b/web/src/pages/basic/basic.tsx
index 5657b210..6d78b223 100644
--- a/web/src/pages/basic/basic.tsx
+++ b/web/src/pages/basic/basic.tsx
@@ -60,7 +60,7 @@ const Basic: FC = () => {
return (
-
+
@@ -72,7 +72,7 @@ const Basic: FC = () => {
type="number"
value={minToken}
onChange={(e) => setMinToken(e.target.value)}
- className="dark:bg-black-1 w-full rounded-md bg-gray-200 p-3 outline-none"
+ className="w-full rounded-md bg-gray-200 p-3 outline-none dark:bg-black-1"
/>
@@ -84,7 +84,7 @@ const Basic: FC = () => {
type="number"
value={maxToken}
onChange={(e) => setMaxToken(e.target.value)}
- className="dark:bg-black-1 w-full rounded-md bg-gray-200 p-3 outline-none"
+ className="w-full rounded-md bg-gray-200 p-3 outline-none dark:bg-black-1"
/>
@@ -96,14 +96,14 @@ const Basic: FC = () => {
type="text"
value={service}
onChange={(e) => setService(e.target.value)}
- className="dark:bg-black-1 w-full rounded-md bg-gray-200 p-3 outline-none"
+ className="w-full rounded-md bg-gray-200 p-3 outline-none dark:bg-black-1"
/>
{messages.map((message) =>
message.role === 'user' ? (
@@ -131,7 +131,7 @@ const Basic: FC = () => {
value={input}
onChange={(e) => setInput(e.target.value)}
rows={2}
- className="dark:bg-black-1 w-full resize-none rounded-md bg-gray-200 p-4 pr-16 outline-none"
+ className="w-full resize-none rounded-md bg-gray-200 p-4 pr-16 outline-none dark:bg-black-1"
/>