hit-server-manager/frontend/src/components/header/ServerModal.tsx

118 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { ChangeEvent, FormEvent } from "react";
import type { Applications, DatabaseType, ServersType } from "../../types/enums";
import type { ServerFormState } from "./types";
interface ServerModalProps {
isOpen: boolean;
form: ServerFormState;
loading: boolean;
onClose: () => void;
onChange: (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => void;
onSubmit: (event: FormEvent) => void;
serverTypeOptions: ServersType[];
applicationOptions: Applications[];
databaseOptions: DatabaseType[];
}
export const ServerModal = ({
isOpen,
form,
loading,
onClose,
onChange,
onSubmit,
serverTypeOptions,
applicationOptions,
databaseOptions,
}: ServerModalProps) => {
if (!isOpen) return null;
return (
<div className={Styles.modalOverlay} role="dialog" aria-modal="true">
<div className={Styles.modal}>
<div className={Styles.modalHeader}>
<h2 className={Styles.modalTitle}>Adicionar novo servidor</h2>
<button type="button" onClick={onClose} className={Styles.closeButton} aria-label="Fechar modal">
×
</button>
</div>
<form onSubmit={onSubmit} className={Styles.form}>
<div className={Styles.formGrid}>
<div className={Styles.field}>
<label htmlFor="name" className={Styles.label}>Nome</label>
<input id="name" name="name" className={Styles.input} value={form.name} onChange={onChange} required />
</div>
<div className={Styles.field}>
<label htmlFor="ip" className={Styles.label}>IP</label>
<input id="ip" name="ip" className={Styles.input} value={form.ip} onChange={onChange} placeholder="192.168.0.10" required />
</div>
</div>
<div className={Styles.formGrid}>
<div className={Styles.field}>
<label htmlFor="port" className={Styles.label}>Porta</label>
<input id="port" name="port" type="number" min="1" className={Styles.input} value={form.port} onChange={onChange} required />
</div>
<div className={Styles.field}>
<label htmlFor="user" className={Styles.label}>Usuário</label>
<input id="user" name="user" className={Styles.input} value={form.user} onChange={onChange} required />
</div>
</div>
<div className={Styles.field}>
<label htmlFor="password" className={Styles.label}>Senha</label>
<input id="password" name="password" type="password" className={Styles.input} value={form.password} onChange={onChange} required />
</div>
<div className={Styles.formGrid}>
<div className={Styles.field}>
<label htmlFor="type" className={Styles.label}>Tipo</label>
<select id="type" name="type" className={Styles.select} value={form.type} onChange={onChange}>
{serverTypeOptions.map((option) => (
<option key={option} value={option}>{option.toLowerCase()}</option>
))}
</select>
</div>
<div className={Styles.field}>
<label htmlFor="application" className={Styles.label}>Aplicação</label>
<select id="application" name="application" className={Styles.select} value={form.application} onChange={onChange}>
{applicationOptions.map((option) => (
<option key={option} value={option}>{option.toLowerCase()}</option>
))}
</select>
</div>
</div>
<div className={Styles.field}>
<label htmlFor="dbType" className={Styles.label}>Banco de dados</label>
<select id="dbType" name="dbType" className={Styles.select} value={form.dbType} onChange={onChange}>
{databaseOptions.map((option) => (
<option key={option} value={option}>{option.toLowerCase()}</option>
))}
</select>
</div>
<div className={Styles.modalActions}>
<button type="button" className={Styles.secondaryButton} onClick={onClose}>Cancelar</button>
<button type="submit" className={Styles.primaryButton} disabled={loading}>
{loading ? "Salvando..." : "Salvar servidor"}
</button>
</div>
</form>
</div>
</div>
);
};
const Styles = {
modalOverlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4",
modal: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl",
modalHeader: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
modalTitle: "text-lg font-semibold text-text",
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
form: "pt-4 space-y-4",
formGrid: "grid gap-4 md:grid-cols-2",
field: "flex flex-col gap-2",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
input: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent",
select: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent capitalize",
modalActions: "flex justify-end gap-3 pt-2",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg disabled:opacity-50",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
};