Frontend: - Implementar máscara de entrada de telefone para números BR ((XX) XXXXX-XXXX). - Atualizar formulário de cadastro para enviar dados completos do perfil do candidato (endereço, formação, habilidades, etc.). - Corrigir problemas de idioma misto na página de Detalhes da Vaga e adicionar traduções faltantes. Backend: - Atualizar modelo de Usuário, Entidade e DTOs para incluir campos de perfil (Data de Nascimento, Endereço, Formação, etc.). - Atualizar UserRepository para persistir e recuperar os dados estendidos do usuário no PostgreSQL. - Atualizar RegisterCandidateUseCase para mapear campos de entrada para a entidade Usuário.
122 lines
4.5 KiB
TypeScript
122 lines
4.5 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import { cn } from "@/lib/utils"
|
|
import { Input } from "@/components/ui/input"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
|
|
const countries = [
|
|
{ value: "55", label: "Brazil (+55)", flag: "🇧🇷" },
|
|
{ value: "1", label: "USA (+1)", flag: "🇺🇸" },
|
|
{ value: "351", label: "Portugal (+351)", flag: "🇵🇹" },
|
|
{ value: "44", label: "UK (+44)", flag: "🇬🇧" },
|
|
{ value: "33", label: "France (+33)", flag: "🇫🇷" },
|
|
{ value: "49", label: "Germany (+49)", flag: "🇩🇪" },
|
|
{ value: "34", label: "Spain (+34)", flag: "🇪🇸" },
|
|
{ value: "39", label: "Italy (+39)", flag: "🇮🇹" },
|
|
{ value: "81", label: "Japan (+81)", flag: "🇯🇵" },
|
|
{ value: "86", label: "China (+86)", flag: "🇨🇳" },
|
|
{ value: "91", label: "India (+91)", flag: "🇮🇳" },
|
|
{ value: "54", label: "Argentina (+54)", flag: "🇦🇷" },
|
|
{ value: "52", label: "Mexico (+52)", flag: "🇲🇽" },
|
|
{ value: "598", label: "Uruguay (+598)", flag: "🇺🇾" },
|
|
]
|
|
|
|
interface PhoneInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {
|
|
value?: string
|
|
onChangeValue?: (value: string) => void
|
|
}
|
|
|
|
export function PhoneInput({ className, value, onChangeValue, ...props }: PhoneInputProps) {
|
|
const [countryCode, setCountryCode] = React.useState("55")
|
|
const [phoneNumber, setPhoneNumber] = React.useState("")
|
|
|
|
const maskPhone = (value: string, code: string) => {
|
|
if (code === "55") {
|
|
return value
|
|
.replace(/\D/g, "")
|
|
.replace(/^(\d{2})(\d)/, "($1) $2")
|
|
.replace(/(\d)(\d{4})$/, "$1-$2");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
// Parse initial value
|
|
React.useEffect(() => {
|
|
if (value) {
|
|
const country = countries.find((c) => value.startsWith("+" + c.value) || value.startsWith(c.value)) // Handle with or without +
|
|
if (country) {
|
|
setCountryCode(country.value)
|
|
// Remove code and +, keep only numbers
|
|
const cleanNumber = value.replace(new RegExp(`^\\+?${country.value}`), "")
|
|
const masked = maskPhone(cleanNumber, country.value)
|
|
setPhoneNumber(masked)
|
|
} else {
|
|
setPhoneNumber(value)
|
|
}
|
|
}
|
|
}, [value])
|
|
|
|
|
|
const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const rawInput = e.target.value;
|
|
const onlyNums = rawInput.replace(/\D/g, "");
|
|
|
|
// Limit length for Brazil (11 digits max)
|
|
if (countryCode === "55" && onlyNums.length > 11) return;
|
|
|
|
const masked = maskPhone(onlyNums, countryCode);
|
|
setPhoneNumber(masked)
|
|
|
|
if (onChangeValue) {
|
|
onChangeValue(`${countryCode}${onlyNums}`)
|
|
}
|
|
}
|
|
|
|
const handleCountryChange = (newCode: string) => {
|
|
setCountryCode(newCode)
|
|
if (onChangeValue) {
|
|
onChangeValue(`${newCode}${phoneNumber}`)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={cn("flex rounded-md shadow-sm", className)}>
|
|
<Select value={countryCode} onValueChange={handleCountryChange}>
|
|
<SelectTrigger className="w-[100px] border-r-0 rounded-r-none focus:ring-0">
|
|
<SelectValue placeholder="Code">
|
|
<span className="flex items-center gap-1">
|
|
<span>{countries.find((c) => c.value === countryCode)?.flag}</span>
|
|
<span>+{countryCode}</span>
|
|
</span>
|
|
</SelectValue>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{countries.map((country) => (
|
|
<SelectItem key={country.value} value={country.value}>
|
|
<span className="flex items-center gap-2">
|
|
<span>{country.flag}</span>
|
|
<span>{country.label}</span>
|
|
</span>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<Input
|
|
{...props}
|
|
className="flex-1 rounded-l-none focus-visible:ring-0 focus-visible:ring-offset-0" // Remove ring collision
|
|
placeholder="Phone number"
|
|
type="tel"
|
|
value={phoneNumber}
|
|
onChange={handlePhoneChange}
|
|
maxLength={15}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|