import { type ChangeEvent, type FormEvent, useEffect, useState } from "react"; import toast from "react-hot-toast"; import api, { logout as requestLogout } 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"; import { useNavigate } from "react-router-dom"; type ModalType = "addServer" | "editProfile" | "bulkImport" | null; interface HeaderProps { currentUser: User | null; userError: string | null; onServerCreated?: () => Promise | 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 navigate = useNavigate(); const [isMenuOpen, setMenuOpen] = useState(false); const [activeModal, setActiveModal] = useState(null); const [serverForm, setServerForm] = useState(defaultServerForm); const [profileForm, setProfileForm] = useState(defaultProfileForm); const [serverLoading, setServerLoading] = useState(false); const [profileLoading, setProfileLoading] = useState(false); const [bulkFile, setBulkFile] = useState(null); const [bulkLoading, setBulkLoading] = useState(false); const [bulkResult, setBulkResult] = useState(null); const [bulkError, setBulkError] = useState(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) => { const { name, value } = event.target; setServerForm((prev) => ({ ...prev, [name]: value })); }; const handleProfileFormChange = (event: ChangeEvent) => { 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(`/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("/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); }; const handleLogout = async () => { setMenuOpen(false); setActiveModal(null); try { await requestLogout(); toast.success("Sessão encerrada com sucesso."); } catch (err: any) { const message = err?.response?.data?.message || "Não foi possível encerrar a sessão."; toast.error(message); } finally { navigate("/login", { replace: true }); } }; return ( <>
openModal("addServer")} onEditProfile={() => openModal("editProfile")} onBulkCreate={() => openModal("bulkImport")} onLogout={handleLogout} />
); }; const Styles = { wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm", };