hit-server-manager/frontend/src/components/Header.tsx

235 lines
8.5 KiB
TypeScript
Raw Normal View History

import { type ChangeEvent, type FormEvent, useEffect, useState } from "react";
import toast from "react-hot-toast";
import api from "../Api";
import { HeaderActions } from "./header/HeaderActions";
import { HeaderBrand } from "./header/HeaderBrand";
import { ProfileModal } from "./header/ProfileModal";
import { ServerModal } from "./header/ServerModal";
import { BulkImportModal } from "./header/BulkImportModal";
import type { Applications, DatabaseType, ServersType } from "../types/enums";
import type { User } from "../types/User";
import type { ProfileFormState, ServerFormState } from "./header/types";
import type { BulkImportResult } from "../types/BulkImport";
type ModalType = "addServer" | "editProfile" | "bulkImport" | null;
interface HeaderProps {
currentUser: User | null;
userError: string | null;
onServerCreated?: () => Promise<void> | void;
onProfileUpdated?: (user: User) => void;
}
const defaultServerForm: ServerFormState = {
name: "",
ip: "",
port: "",
user: "",
password: "",
type: "PRODUCTION",
application: "ASTERISK",
dbType: "MYSQL",
};
const defaultProfileForm: ProfileFormState = {
firstName: "",
lastName: "",
email: "",
password: "",
};
const serverTypeOptions: ServersType[] = ["PRODUCTION", "HOMOLOGATION", "DATABASE"];
const applicationOptions: Applications[] = ["ASTERISK", "HITMANAGER", "HITMANAGER_V2", "OMNIHIT", "HITPHONE"];
const databaseOptions: DatabaseType[] = ["MYSQL", "POSTGRESQL", "SQLSERVER", "ORACLE", "REDIS", "MONGODB", "MARIADB", "NONE"];
export const Header = ({ currentUser, userError, onServerCreated, onProfileUpdated }: HeaderProps) => {
const [isMenuOpen, setMenuOpen] = useState(false);
const [activeModal, setActiveModal] = useState<ModalType>(null);
const [serverForm, setServerForm] = useState<ServerFormState>(defaultServerForm);
const [profileForm, setProfileForm] = useState<ProfileFormState>(defaultProfileForm);
const [serverLoading, setServerLoading] = useState(false);
const [profileLoading, setProfileLoading] = useState(false);
const [bulkFile, setBulkFile] = useState<File | null>(null);
const [bulkLoading, setBulkLoading] = useState(false);
const [bulkResult, setBulkResult] = useState<BulkImportResult | null>(null);
const [bulkError, setBulkError] = useState<string | null>(null);
useEffect(() => {
if (currentUser) {
setProfileForm((prev) => ({
...prev,
firstName: currentUser.firstName ?? "",
lastName: currentUser.lastName ?? "",
email: currentUser.email ?? "",
}));
}
}, [currentUser]);
const toggleMenu = () => setMenuOpen((prev) => !prev);
const openModal = (modal: ModalType) => {
setMenuOpen(false);
setActiveModal(modal);
};
const closeModal = () => {
setActiveModal(null);
setServerForm(defaultServerForm);
setProfileForm((prev) => ({ ...prev, password: "" }));
setBulkFile(null);
setBulkResult(null);
setBulkError(null);
};
const handleServerFormChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = event.target;
setServerForm((prev) => ({ ...prev, [name]: value }));
};
const handleProfileFormChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setProfileForm((prev) => ({ ...prev, [name]: value }));
};
const handleServerSubmit = async (event: FormEvent) => {
event.preventDefault();
setServerLoading(true);
try {
await api.post("/api/servers", {
...serverForm,
port: Number(serverForm.port),
});
toast.success("Servidor criado com sucesso!");
setServerForm(defaultServerForm);
setActiveModal(null);
if (onServerCreated) {
await Promise.resolve(onServerCreated());
}
} catch (err: any) {
const message = err?.response?.data?.message || "Falha ao criar servidor.";
toast.error(message);
} finally {
setServerLoading(false);
}
};
const handleProfileSubmit = async (event: FormEvent) => {
event.preventDefault();
if (!currentUser) {
toast.error("Usuário não identificado.");
return;
}
setProfileLoading(true);
try {
const { data } = await api.put<User>(`/api/users/${currentUser.id}`, {
firstName: profileForm.firstName,
lastName: profileForm.lastName,
email: profileForm.email,
password: profileForm.password,
});
toast.success("Perfil atualizado com sucesso!");
setProfileForm((prev) => ({ ...prev, password: "" }));
setActiveModal(null);
onProfileUpdated?.(data);
} catch (err: any) {
const message = err?.response?.data?.message || "Falha ao atualizar o perfil.";
toast.error(message);
} finally {
setProfileLoading(false);
}
};
const handleBulkSubmit = async () => {
if (!bulkFile) {
toast.error("Selecione um arquivo CSV para importar.");
return;
}
setBulkLoading(true);
setBulkError(null);
try {
const formData = new FormData();
formData.append("file", bulkFile);
const { data } = await api.post<BulkImportResult>("/api/servers/bulk", formData, {
headers: { "Content-Type": "multipart/form-data" },
});
setBulkResult(data);
toast.success(`Importação concluída: ${data.succeeded} sucesso(s).`);
await Promise.resolve(onServerCreated?.());
} catch (err: any) {
const message = err?.response?.data?.message || "Falha ao importar servidores.";
setBulkError(message);
toast.error(message);
} finally {
setBulkLoading(false);
}
};
const handleDownloadTemplate = () => {
const sample = [
"name;ip;port;user;password;type;application;dbType",
"app-server;192.168.0.10;22;deploy;changeMe;PRODUCTION;HITMANAGER;POSTGRESQL",
].join("\n");
const blob = new Blob([sample], { type: "text/csv;charset=utf-8;" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "servers_template.csv";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return (
<>
<header className={Styles.wrapper}>
<HeaderBrand />
<HeaderActions
isMenuOpen={isMenuOpen}
onToggleMenu={toggleMenu}
onAddServer={() => openModal("addServer")}
onEditProfile={() => openModal("editProfile")}
onBulkCreate={() => openModal("bulkImport")}
/>
</header>
<ServerModal
isOpen={activeModal === "addServer"}
form={serverForm}
loading={serverLoading}
onClose={closeModal}
onChange={handleServerFormChange}
onSubmit={handleServerSubmit}
serverTypeOptions={serverTypeOptions}
applicationOptions={applicationOptions}
databaseOptions={databaseOptions}
/>
<ProfileModal
isOpen={activeModal === "editProfile"}
currentUser={currentUser}
userError={userError}
form={profileForm}
loading={profileLoading}
onClose={closeModal}
onChange={handleProfileFormChange}
onSubmit={handleProfileSubmit}
/>
<BulkImportModal
isOpen={activeModal === "bulkImport"}
file={bulkFile}
loading={bulkLoading}
result={bulkResult}
error={bulkError}
onClose={closeModal}
onFileChange={setBulkFile}
onSubmit={handleBulkSubmit}
onDownloadTemplate={handleDownloadTemplate}
/>
</>
);
};
const Styles = {
wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm",
};