2025-12-16 16:48:47 +00:00
import { type ChangeEvent , type FormEvent , useEffect , useState } from "react" ;
import toast from "react-hot-toast" ;
import api from "../Api" ;
import type { Applications , DatabaseType , ServersType } from "../types/enums" ;
import type { User } from "../types/User" ;
2025-12-16 16:34:06 +00:00
2025-12-16 16:48:47 +00:00
type ModalType = "addServer" | "editProfile" | null ;
interface HeaderProps {
currentUser : User | null ;
userError : string | null ;
onServerCreated ? : ( ) = > Promise < void > | void ;
onProfileUpdated ? : ( user : User ) = > void ;
}
type ServerFormState = {
name : string ;
ip : string ;
port : string ;
user : string ;
password : string ;
type : ServersType ;
application : Applications ;
dbType : DatabaseType ;
} ;
type ProfileFormState = {
firstName : string ;
lastName : string ;
email : string ;
password : string ;
} ;
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 ) = > {
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 ) ;
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 : "" } ) ) ;
} ;
const handleServerFormChange = ( e : ChangeEvent < HTMLInputElement | HTMLSelectElement > ) = > {
const { name , value } = e . target ;
setServerForm ( ( prev ) = > ( { . . . prev , [ name ] : value } ) ) ;
} ;
const handleProfileFormChange = ( e : ChangeEvent < HTMLInputElement > ) = > {
const { name , value } = e . 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 renderModalContent = ( ) = > {
if ( activeModal === "addServer" ) {
return (
< form onSubmit = { handleServerSubmit } 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 = { serverForm . name } onChange = { handleServerFormChange } required / >
< / div >
< div className = { Styles . field } >
< label htmlFor = "ip" className = { Styles . label } > IP < / label >
< input id = "ip" name = "ip" className = { Styles . input } value = { serverForm . ip } onChange = { handleServerFormChange } 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 = { serverForm . port } onChange = { handleServerFormChange } required / >
< / div >
< div className = { Styles . field } >
< label htmlFor = "user" className = { Styles . label } > Usu á rio < / label >
< input id = "user" name = "user" className = { Styles . input } value = { serverForm . user } onChange = { handleServerFormChange } 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 = { serverForm . password } onChange = { handleServerFormChange } 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 = { serverForm . type } onChange = { handleServerFormChange } >
{ 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 = { serverForm . application } onChange = { handleServerFormChange } >
{ 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 = { serverForm . dbType } onChange = { handleServerFormChange } >
{ databaseOptions . map ( ( option ) = > (
< option key = { option } value = { option } > { option . toLowerCase ( ) } < / option >
) ) }
< / select >
< / div >
< div className = { Styles . modalActions } >
< button type = "button" className = { Styles . secondaryButton } onClick = { closeModal } > Cancelar < / button >
< button type = "submit" className = { Styles . primaryButton } disabled = { serverLoading } >
{ serverLoading ? "Salvando..." : "Salvar servidor" }
< / button >
< / div >
< / form >
) ;
}
if ( activeModal === "editProfile" ) {
if ( userError ) {
return < p className = { Styles . helperText } > { userError } < / p > ;
}
return (
< form onSubmit = { handleProfileSubmit } className = { Styles . form } >
< div className = { Styles . formGrid } >
< div className = { Styles . field } >
< label htmlFor = "firstName" className = { Styles . label } > Nome < / label >
< input id = "firstName" name = "firstName" className = { Styles . input } value = { profileForm . firstName } onChange = { handleProfileFormChange } required disabled = { ! currentUser } / >
< / div >
< div className = { Styles . field } >
< label htmlFor = "lastName" className = { Styles . label } > Sobrenome < / label >
< input id = "lastName" name = "lastName" className = { Styles . input } value = { profileForm . lastName } onChange = { handleProfileFormChange } required disabled = { ! currentUser } / >
< / div >
< / div >
< div className = { Styles . field } >
< label htmlFor = "email" className = { Styles . label } > Email < / label >
< input id = "email" name = "email" type = "email" className = { Styles . input } value = { profileForm . email } onChange = { handleProfileFormChange } required disabled = { ! currentUser } / >
< / div >
< div className = { Styles . field } >
< label htmlFor = "password" className = { Styles . label } > Nova senha < / label >
< input id = "password" name = "password" type = "password" className = { Styles . input } value = { profileForm . password } onChange = { handleProfileFormChange } placeholder = "Informe uma nova senha" required disabled = { ! currentUser } / >
< p className = { Styles . helperText } > Informe uma nova senha para confirmar a altera ç ã o . < / p >
< / div >
< div className = { Styles . modalActions } >
< button type = "button" className = { Styles . secondaryButton } onClick = { closeModal } > Cancelar < / button >
< button type = "submit" className = { Styles . primaryButton } disabled = { profileLoading || ! currentUser } >
{ profileLoading ? "Salvando..." : "Salvar alterações" }
< / button >
< / div >
< / form >
) ;
}
return null ;
} ;
2025-12-16 16:34:06 +00:00
return (
2025-12-16 16:48:47 +00:00
< >
< header className = { Styles . wrapper } >
< div className = { Styles . brand } >
< img src = "/logo.webp" alt = "Logo Hit Communications" className = { Styles . logo } / >
< div >
< p className = { Styles . title } > Hit Communications < / p >
< p className = { Styles . subtitle } > Servers Manager < / p >
< / div >
2025-12-16 16:34:06 +00:00
< / div >
2025-12-16 16:48:47 +00:00
< div className = { Styles . actions } >
< div className = "relative" >
< button
type = "button"
className = { Styles . menuTrigger }
aria - haspopup = "menu"
aria - expanded = { isMenuOpen }
onClick = { toggleMenu }
>
Op ç õ es
< / button >
{ isMenuOpen && (
< div className = { Styles . dropdown } role = "menu" >
< button
type = "button"
className = { Styles . dropdownItem }
onClick = { ( ) = > openModal ( "addServer" ) }
>
Adicionar servidor
< / button >
< button
type = "button"
className = { Styles . dropdownItem }
onClick = { ( ) = > openModal ( "editProfile" ) }
>
Editar perfil
< / button >
< / div >
) }
< / div >
< button type = "button" className = { Styles . logoutButton } >
Sair
2025-12-16 16:34:06 +00:00
< / button >
2025-12-16 16:48:47 +00:00
< / div >
< / header >
{ activeModal && (
< div className = { Styles . modalOverlay } role = "dialog" aria - modal = "true" >
< div className = { Styles . modal } >
< div className = { Styles . modalHeader } >
< h2 className = { Styles . modalTitle } >
{ activeModal === "addServer" ? "Adicionar novo servidor" : "Editar perfil" }
< / h2 >
< button type = "button" onClick = { closeModal } className = { Styles . closeButton } aria - label = "Fechar modal" >
×
2025-12-16 16:34:06 +00:00
< / button >
< / div >
2025-12-16 16:48:47 +00:00
< div className = { Styles . modalBody } > { renderModalContent ( ) } < / div >
< / div >
2025-12-16 16:34:06 +00:00
< / div >
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" ,
brand : "flex items-center gap-3" ,
logo : "h-10 w-10 object-contain" ,
title : "text-base font-semibold text-text" ,
subtitle : "text-xs uppercase tracking-wide text-text-secondary" ,
actions : "flex items-center gap-3" ,
menuTrigger : "rounded-lg border border-cardBorder bg-white/70 px-4 py-2 text-sm font-medium text-text flex items-center gap-2 hover:bg-white transition-colors" ,
2025-12-16 16:48:47 +00:00
dropdown : "absolute right-0 mt-2 w-48 rounded-lg border border-cardBorder bg-white py-2 shadow-lg z-10" ,
2025-12-16 16:34:06 +00:00
dropdownItem : "w-full px-4 py-2 text-left text-sm text-text-secondary hover:bg-bg hover:text-text transition-colors" ,
logoutButton : "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-hover" ,
2025-12-16 16:48:47 +00:00
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" ,
modalBody : "pt-4" ,
form : "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" ,
helperText : "text-xs text-text-secondary" ,
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" ,
2025-12-16 16:34:06 +00:00
} ;