// Importa un listado de países con sus prefijos telefónicos
import { COUNTRIES_AND_PHONE } from '@/constants/countriesPhone'
import type { ShallowRef } from 'vue'
// Extrae todos los prefijos telefónicos en un array
const codes = COUNTRIES_AND_PHONE.map((c) => c.codePhone)
* Hook personalizado para manejar formularios reactivos con validación y soporte de internacionalización.
* @param defaults Valores por defecto del formulario
* @param locales Referencia reactiva a los textos localizados para los mensajes de validación
* @returns Un objeto con el estado del formulario, validez, mensajes de error, etc.
T extends Record<string, unknown>,
L extends Record<string, unknown> = {},
>(defaults: Partial<T>, locales: ShallowRef<L | undefined>) {
// Estado reactivo del formulario, inicializado con los valores por defecto
const formData = shallowReactive<T>({
// Flag para evitar reacciones durante asignaciones programadas
* Rellena el formulario con nuevos datos, y previene efectos secundarios temporales.
* @param data Datos parciales para rellenar el formulario
function fillFormData(data: Partial<T>) {
shouldWatch = false // Desactiva temporalmente los watchers
Object.assign(formData, data) // Asigna los nuevos valores
// Asigna manualmente el campo "code" si existe
if ('code' in data && 'code' in formData) {
;(formData as Record<string, unknown>).code = data.code
// Reactiva los watchers en el siguiente tick del DOM
// Computed que indica si hay campos inválidos en el formulario
const invalidFields = computed(() => {
const checks = validator.validateFields(formData)
// Devuelve true si al menos un campo válido es falso
return Array.from(checks.entries()).some(([key, result]) => {
return key in formData && result.valid !== true
// Mapa reactivo para almacenar los mensajes de validación por campo
const validationMessages = ref<Map<string, string>>(new Map())
// Watcher que valida el formulario cada vez que cambia cualquier campo
validationMessages.value.clear() // Limpia los mensajes anteriores
// Ejecuta la validación con posibles textos localizados
const result = validator.validateFields(newFields, locales)
// Asigna el mensaje de error correspondiente por cada campo
for (const [key] of Object.entries(newFields)) {
const message = result.get(key)?.message || ''
validationMessages.value.set(key, message)
{ deep: true }, // Reacciona a cambios profundos en objetos anidados
// Si el formulario contiene los campos `country` y `code`, sincroniza el código telefónico
if ('country' in formData && 'code' in formData) {
(newCountry: unknown | unknown[]) => {
if (!shouldWatch) return // Ignora si está temporalmente deshabilitado
// Toma el país seleccionado (soporta arrays)
const countryCode = Array.isArray(newCountry) ? newCountry[0] : newCountry
// Busca el código telefónico correspondiente al país
const selected = COUNTRIES_AND_PHONE.find((c) => c.code === countryCode)
// Asigna el código telefónico correspondiente, o vacío si no se encuentra
;(formData as Record<string, unknown>).code = selected?.codePhone ?? ''
{ flush: 'sync' }, // Ejecuta inmediatamente después del cambio (antes del render)
// Devuelve las referencias útiles del formulario
formData, // Estado reactivo del formulario
fillFormData, // Función para rellenar el formulario
codes: 'code' in formData ? codes : undefined, // Lista de códigos solo si se usa el campo "code"
invalidFields, // Computed que indica si hay errores
validationMessages, // Mensajes de validación por campo