"use client"; import { ChevronDown, X } from "lucide-react"; import { useTranslations } from "next-intl"; import { useEffect, useMemo, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useServers } from "@/lib/hooks/use-servers"; import { useSession } from "@/lib/hooks/use-session"; import { COUNTRIES, type Country, DEFAULT_COUNTRY, } from "@/lib/utils/countries"; import { checkPasswordStrength, combinePhoneWithDialCode, formatInternationalPhone, getPhonePlaceholder, unformatPhone, validatePasswordRequirements, } from "@/lib/utils/formatters"; interface RegisterDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } interface ServerSelection { serverName: string; characterName: string; isMain: boolean; } export function RegisterDialog({ open, onOpenChange }: RegisterDialogProps) { const t = useTranslations("auth.register"); const { refreshSession } = useSession(); const { servers, isLoading: serversLoading, error: serversError, } = useServers(); // Form fields const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [phone, setPhone] = useState(""); const [selectedCountry, setSelectedCountry] = useState(DEFAULT_COUNTRY); const [countrySearch, setCountrySearch] = useState(""); // Servers selection const [selectedServers, setSelectedServers] = useState([]); const [serverSearch, setServerSearch] = useState(""); const [showServerDropdown, setShowServerDropdown] = useState(false); // UI state const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [fieldErrors, setFieldErrors] = useState>({}); const [passwordStrength, setPasswordStrength] = useState< "weak" | "medium" | "strong" >("weak"); // Filtered servers based on search const filteredServers = useMemo(() => { if (!serverSearch.trim()) return servers; const searchLower = serverSearch.toLowerCase(); return servers.filter( (s) => s.serverName.toLowerCase().includes(searchLower) && !selectedServers.some((sel) => sel.serverName === s.serverName), ); }, [serverSearch, servers, selectedServers]); // Update password strength useEffect(() => { if (password) { setPasswordStrength(checkPasswordStrength(password)); } else { setPasswordStrength("weak"); } }, [password]); // Reset form when dialog closes useEffect(() => { if (!open) { setUsername(""); setEmail(""); setPassword(""); setConfirmPassword(""); setFirstName(""); setLastName(""); setPhone(""); setSelectedCountry(DEFAULT_COUNTRY); setCountrySearch(""); setSelectedServers([]); setServerSearch(""); setError(null); setFieldErrors({}); setPasswordStrength("weak"); } }, [open]); const addServer = (serverName: string) => { if (selectedServers.some((s) => s.serverName === serverName)) { return; } const isFirst = selectedServers.length === 0; setSelectedServers([ ...selectedServers, { serverName, characterName: "", isMain: isFirst, }, ]); setServerSearch(""); setShowServerDropdown(false); }; const removeServer = (index: number) => { const newServers = selectedServers.filter((_, i) => i !== index); // If we removed the main character and there are still servers, make first one main if (selectedServers[index].isMain && newServers.length > 0) { newServers[0].isMain = true; } setSelectedServers(newServers); }; const updateServerCharacter = (index: number, characterName: string) => { const newServers = [...selectedServers]; newServers[index].characterName = characterName; setSelectedServers(newServers); }; const setMainServer = (index: number) => { const newServers = selectedServers.map((s, i) => ({ ...s, isMain: i === index, })); setSelectedServers(newServers); }; const handlePhoneChange = (value: string) => { const formatted = formatInternationalPhone( value, selectedCountry.dialCode, selectedCountry.code, ); setPhone(formatted); }; // Filtrar países para busca const filteredCountries = useMemo(() => { if (!countrySearch.trim()) return COUNTRIES; const searchLower = countrySearch.toLowerCase(); return COUNTRIES.filter( (c) => c.name.toLowerCase().includes(searchLower) || c.dialCode.includes(searchLower) || c.code.toLowerCase().includes(searchLower), ); }, [countrySearch]); const validateForm = (): boolean => { const errors: Record = {}; if (!username.trim()) { errors.username = "Username é obrigatório"; } else if (username.length < 3 || username.length > 50) { errors.username = "Username deve ter entre 3 e 50 caracteres"; } if (!email.trim()) { errors.email = "Email é obrigatório"; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { errors.email = "Email inválido"; } const passwordValidation = validatePasswordRequirements(password); if (!password) { errors.password = "Senha é obrigatória"; } else if (!passwordValidation.isValid) { errors.password = passwordValidation.errors[0]; } if (password !== confirmPassword) { errors.confirmPassword = t("errors.passwordMismatch"); } if (!firstName.trim()) { errors.firstName = "Nome é obrigatório"; } if (!lastName.trim()) { errors.lastName = "Sobrenome é obrigatório"; } if (selectedServers.length === 0) { errors.servers = t("errors.noServers"); } selectedServers.forEach((server, index) => { if (!server.characterName.trim()) { errors[`server_${index}`] = t("errors.characterNameRequired"); } }); setFieldErrors(errors); return Object.keys(errors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(null); setFieldErrors({}); if (!validateForm()) { return; } setIsLoading(true); try { const response = await fetch("/api/auth/register", { method: "POST", headers: { "Content-Type": "application/json", }, credentials: "include", body: JSON.stringify({ username: username.trim().toLowerCase(), email: email.trim().toLowerCase(), password, firstName: firstName.trim(), lastName: lastName.trim(), phone: phone ? combinePhoneWithDialCode(selectedCountry.dialCode, phone) : undefined, servers: selectedServers.map((s) => ({ serverName: s.serverName, characterName: s.characterName.trim(), isMain: s.isMain, })), }), }); // Verificar se a resposta é JSON antes de fazer parse const contentType = response.headers.get("content-type"); if (!contentType || !contentType.includes("application/json")) { const text = await response.text(); console.error( "Resposta não é JSON do registro:", text.substring(0, 200), ); setError("Erro: resposta inválida do servidor"); setIsLoading(false); return; } const data = await response.json(); if (data.success) { await refreshSession(); onOpenChange(false); window.location.reload(); } else { if (data.details) { // Field-specific errors const detailsErrors: Record = {}; data.details.forEach((detail: { field: string; message: string }) => { detailsErrors[detail.field] = detail.message; }); setFieldErrors(detailsErrors); } else { setError(data.error || "Erro ao criar conta"); } } } catch (_err) { setError("Erro ao conectar com o servidor"); } finally { setIsLoading(false); } }; const getPasswordStrengthColor = () => { switch (passwordStrength) { case "weak": return "bg-red-500"; case "medium": return "bg-yellow-500"; case "strong": return "bg-green-500"; } }; return ( {t("title")} {t("description")}
{/* Credentials Section */}

Credenciais

setUsername(e.target.value)} disabled={isLoading} className={fieldErrors.username ? "border-destructive" : ""} /> {fieldErrors.username && (

{fieldErrors.username}

)}
setEmail(e.target.value)} disabled={isLoading} className={fieldErrors.email ? "border-destructive" : ""} /> {fieldErrors.email && (

{fieldErrors.email}

)}
setPassword(e.target.value)} disabled={isLoading} className={fieldErrors.password ? "border-destructive" : ""} /> {password && (
{t(`passwordStrength.${passwordStrength}`)}
)} {fieldErrors.password && (

{fieldErrors.password}

)}
setConfirmPassword(e.target.value)} disabled={isLoading} className={ fieldErrors.confirmPassword ? "border-destructive" : "" } /> {fieldErrors.confirmPassword && (

{fieldErrors.confirmPassword}

)}
{/* Personal Info Section */}

Dados Pessoais

setFirstName(e.target.value)} disabled={isLoading} className={fieldErrors.firstName ? "border-destructive" : ""} /> {fieldErrors.firstName && (

{fieldErrors.firstName}

)}
setLastName(e.target.value)} disabled={isLoading} className={fieldErrors.lastName ? "border-destructive" : ""} /> {fieldErrors.lastName && (

{fieldErrors.lastName}

)}
{/* Seletor de País */}
setCountrySearch(e.target.value)} className="mb-2" onClick={(e) => e.stopPropagation()} />
{filteredCountries.map((country) => ( { setSelectedCountry(country); setCountrySearch(""); // Reformatar telefone com novo país if (phone) { const unformatted = unformatPhone(phone); const reformatted = formatInternationalPhone( unformatted, country.dialCode, country.code, ); setPhone(reformatted); } }} className="cursor-pointer" > {country.flag} {country.name} {country.dialCode} ))}
{/* Campo de Telefone */} handlePhoneChange(e.target.value)} disabled={isLoading} maxLength={20} className="flex-1" />

{t("phoneHint")}

{fieldErrors.phone && (

{fieldErrors.phone}

)}
{/* Servers Section */}

{t("servers")}

{t("serversDescription")}

{/* Server Search/Add */}
{ setServerSearch(e.target.value); setShowServerDropdown(true); }} onFocus={() => setShowServerDropdown(true)} disabled={isLoading || serversLoading} /> {showServerDropdown && filteredServers.length > 0 && (
{filteredServers.slice(0, 10).map((server) => ( ))}
)}
{/* Selected Servers */} {selectedServers.length > 0 && (
{selectedServers.map((server, index) => (
{server.serverName} {server.isMain && ( {t("mainCharacter")} )}
{!server.isMain && ( )}
updateServerCharacter(index, e.target.value) } disabled={isLoading} className={ fieldErrors[`server_${index}`] ? "border-destructive" : "" } /> {fieldErrors[`server_${index}`] && (

{fieldErrors[`server_${index}`]}

)}
))}
)} {fieldErrors.servers && (

{fieldErrors.servers}

)} {serversError && (

Erro ao carregar servidores: {serversError}

)}
{error && (
{error}
)}
); }