2025-12-16 16:48:47 +00:00
|
|
|
import { type ChangeEvent, type FormEvent, useEffect, useState } from "react";
|
|
|
|
|
import toast from "react-hot-toast";
|
2025-12-16 18:36:02 +00:00
|
|
|
import api, { logout as requestLogout } from "../Api";
|
2025-12-16 16:54:33 +00:00
|
|
|
import { HeaderActions } from "./header/HeaderActions";
|
|
|
|
|
import { HeaderBrand } from "./header/HeaderBrand";
|
|
|
|
|
import { ProfileModal } from "./header/ProfileModal";
|
|
|
|
|
import { ServerModal } from "./header/ServerModal";
|
2025-12-16 17:10:03 +00:00
|
|
|
import { BulkImportModal } from "./header/BulkImportModal";
|
2025-12-16 16:48:47 +00:00
|
|
|
import type { Applications, DatabaseType, ServersType } from "../types/enums";
|
|
|
|
|
import type { User } from "../types/User";
|
2025-12-16 16:54:33 +00:00
|
|
|
import type { ProfileFormState, ServerFormState } from "./header/types";
|
2025-12-16 17:10:03 +00:00
|
|
|
import type { BulkImportResult } from "../types/BulkImport";
|
2025-12-16 18:36:02 +00:00
|
|
|
import { useNavigate } from "react-router-dom";
|
2025-12-16 16:34:06 +00:00
|
|
|
|
2025-12-16 17:10:03 +00:00
|
|
|
type ModalType = "addServer" | "editProfile" | "bulkImport" | null;
|
2025-12-16 16:48:47 +00:00
|
|
|
|
|
|
|
|
interface HeaderProps {
|
|
|
|
|
currentUser: User | null;
|
|
|
|
|
userError: string | null;
|
|
|
|
|
onServerCreated?: () => Promise<void> | void;
|
|
|
|
|
onProfileUpdated?: (user: User) => void;
|
2025-12-16 21:09:54 +00:00
|
|
|
serverTypeOptions?: ServersType[];
|
|
|
|
|
applicationOptions?: Applications[];
|
|
|
|
|
databaseOptions?: DatabaseType[];
|
2025-12-16 16:48:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const defaultServerForm: ServerFormState = {
|
|
|
|
|
name: "",
|
|
|
|
|
ip: "",
|
|
|
|
|
port: "",
|
|
|
|
|
user: "",
|
|
|
|
|
password: "",
|
|
|
|
|
type: "PRODUCTION",
|
|
|
|
|
application: "ASTERISK",
|
|
|
|
|
dbType: "MYSQL",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const defaultProfileForm: ProfileFormState = {
|
|
|
|
|
firstName: "",
|
|
|
|
|
lastName: "",
|
|
|
|
|
email: "",
|
|
|
|
|
password: "",
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-16 21:09:54 +00:00
|
|
|
export const Header = ({
|
|
|
|
|
currentUser,
|
|
|
|
|
userError,
|
|
|
|
|
onServerCreated,
|
|
|
|
|
onProfileUpdated,
|
|
|
|
|
serverTypeOptions = [],
|
|
|
|
|
applicationOptions = [],
|
|
|
|
|
databaseOptions = [],
|
|
|
|
|
}: HeaderProps) => {
|
2025-12-16 18:36:02 +00:00
|
|
|
const navigate = useNavigate();
|
2025-12-16 16:34:06 +00:00
|
|
|
const [isMenuOpen, setMenuOpen] = useState(false);
|
2025-12-16 16:48:47 +00:00
|
|
|
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);
|
2025-12-16 17:10:03 +00:00
|
|
|
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);
|
2025-12-16 16:48:47 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (currentUser) {
|
|
|
|
|
setProfileForm((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
firstName: currentUser.firstName ?? "",
|
|
|
|
|
lastName: currentUser.lastName ?? "",
|
|
|
|
|
email: currentUser.email ?? "",
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}, [currentUser]);
|
2025-12-16 16:34:06 +00:00
|
|
|
|
|
|
|
|
const toggleMenu = () => setMenuOpen((prev) => !prev);
|
2025-12-16 16:48:47 +00:00
|
|
|
const openModal = (modal: ModalType) => {
|
|
|
|
|
setMenuOpen(false);
|
|
|
|
|
setActiveModal(modal);
|
|
|
|
|
};
|
|
|
|
|
const closeModal = () => {
|
|
|
|
|
setActiveModal(null);
|
|
|
|
|
setServerForm(defaultServerForm);
|
|
|
|
|
setProfileForm((prev) => ({ ...prev, password: "" }));
|
2025-12-16 17:10:03 +00:00
|
|
|
setBulkFile(null);
|
|
|
|
|
setBulkResult(null);
|
|
|
|
|
setBulkError(null);
|
2025-12-16 16:48:47 +00:00
|
|
|
};
|
|
|
|
|
|
2025-12-16 16:54:33 +00:00
|
|
|
const handleServerFormChange = (event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
|
|
|
|
const { name, value } = event.target;
|
2025-12-16 16:48:47 +00:00
|
|
|
setServerForm((prev) => ({ ...prev, [name]: value }));
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-16 16:54:33 +00:00
|
|
|
const handleProfileFormChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const { name, value } = event.target;
|
2025-12-16 16:48:47 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-16 17:10:03 +00:00
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-16 18:36:02 +00:00
|
|
|
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 });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-16 16:34:06 +00:00
|
|
|
return (
|
2025-12-16 16:48:47 +00:00
|
|
|
<>
|
|
|
|
|
<header className={Styles.wrapper}>
|
2025-12-16 16:54:33 +00:00
|
|
|
<HeaderBrand />
|
|
|
|
|
<HeaderActions
|
|
|
|
|
isMenuOpen={isMenuOpen}
|
|
|
|
|
onToggleMenu={toggleMenu}
|
|
|
|
|
onAddServer={() => openModal("addServer")}
|
|
|
|
|
onEditProfile={() => openModal("editProfile")}
|
2025-12-16 17:10:03 +00:00
|
|
|
onBulkCreate={() => openModal("bulkImport")}
|
2025-12-16 18:36:02 +00:00
|
|
|
onLogout={handleLogout}
|
2025-12-16 16:54:33 +00:00
|
|
|
/>
|
2025-12-16 16:48:47 +00:00
|
|
|
</header>
|
|
|
|
|
|
2025-12-16 16:54:33 +00:00
|
|
|
<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}
|
|
|
|
|
/>
|
2025-12-16 17:10:03 +00:00
|
|
|
|
|
|
|
|
<BulkImportModal
|
|
|
|
|
isOpen={activeModal === "bulkImport"}
|
|
|
|
|
file={bulkFile}
|
|
|
|
|
loading={bulkLoading}
|
|
|
|
|
result={bulkResult}
|
|
|
|
|
error={bulkError}
|
|
|
|
|
onClose={closeModal}
|
|
|
|
|
onFileChange={setBulkFile}
|
|
|
|
|
onSubmit={handleBulkSubmit}
|
|
|
|
|
onDownloadTemplate={handleDownloadTemplate}
|
|
|
|
|
/>
|
2025-12-16 16:48:47 +00:00
|
|
|
</>
|
2025-12-16 16:34:06 +00:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Styles = {
|
|
|
|
|
wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm",
|
|
|
|
|
};
|