form create
This commit is contained in:
parent
820f11a5a0
commit
aab729cd4a
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"action": "/api/formulario/send",
|
||||
"submit_label": "form.submit",
|
||||
"steps": [
|
||||
{
|
||||
"label": "form.step1",
|
||||
"column": 2,
|
||||
"fields": [
|
||||
{ "key": "nombre", "label": "form.nombre", "type": "text", "required": true },
|
||||
{ "key": "apellido", "label": "form.apellido", "type": "text", "required": true },
|
||||
{ "key": "fecha_nacimiento", "label": "form.fecha_nacimiento", "type": "date", "required": true },
|
||||
{ "key": "nacionalidad", "label": "form.nacionalidad", "type": "text", "required": true },
|
||||
{ "key": "documentos", "label": "form.documentos", "type": "text", "required": true, "colspan": 2 },
|
||||
{
|
||||
"key": "sexo",
|
||||
"label": "form.sexo",
|
||||
"type": "radio",
|
||||
"colspan": 2,
|
||||
"required": true,
|
||||
"options": [
|
||||
{ "value": "M", "label": "form.sexo.m" },
|
||||
{ "value": "F", "label": "form.sexo.f" },
|
||||
{ "value": "Otro", "label": "form.sexo.o" }
|
||||
]
|
||||
},
|
||||
{ "key": "direccion_completa", "label": "form.direccion", "type": "text", "required": true, "colspan": 2 },
|
||||
{ "key": "ciudad", "label": "form.ciudad", "type": "text", "required": true },
|
||||
{ "key": "estado", "label": "form.estado", "type": "text", "required": true },
|
||||
{ "key": "pais", "label": "form.pais", "type": "text", "required": true },
|
||||
{ "key": "postal", "label": "form.codigo_postal", "type": "text", "required": true },
|
||||
{ "key": "telefono", "label": "form.mobile", "type": "text", "required": true },
|
||||
{ "key": "whatsapp", "label": "form.whatsapp", "type": "text", "required": true },
|
||||
{ "key": "email", "label": "form.correo", "type": "email", "required": true, "colspan": 2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "form.step2",
|
||||
"column": 2,
|
||||
"fields": [
|
||||
{ "key": "profesion", "label": "form.profesion", "type": "text", "required": true },
|
||||
{ "key": "lugar_trabajo_actual", "label": "form.lugar_trabajo_actual", "type": "text", "required": true },
|
||||
{ "key": "nivel_academico", "label": "form.nivel_academico", "type": "text", "required": true, "colspan": 2 },
|
||||
{
|
||||
"key": "idioma",
|
||||
"label": "form.idioma",
|
||||
"type": "checkbox",
|
||||
"required": true,
|
||||
"colspan": 2,
|
||||
"levels": {
|
||||
"options": [
|
||||
{ "value": "Basico", "label": "form.nivel.basico" },
|
||||
{ "value": "Intermedio", "label": "form.nivel.intermedio" },
|
||||
{ "value": "Avanzado", "label": "form.nivel.avanzado" }
|
||||
]
|
||||
},
|
||||
"options": [
|
||||
{ "value": "Espanol", "label": "form.idioma.esp" },
|
||||
{ "value": "Ingles", "label": "form.idioma.ing" },
|
||||
{ "value": "Hebreo", "label": "form.idioma.heb" },
|
||||
{ "value": "Portugues", "label": "form.idioma.port" },
|
||||
{ "value": "Otro", "label": "form.idioma.otro" }
|
||||
]
|
||||
},
|
||||
{ "key": "idioma_otro", "label": "form.idioma_otro", "type": "text", "required": false, "colspan": 2, "showWhen": { "field": "idioma", "value": "Otro" } },
|
||||
{ "key": "voluntariado_anterior", "label": "form.voluntariado_anterior", "type": "radio", "required": true, "colspan": 2, "options":
|
||||
[
|
||||
{ "value": "si", "label": "form.voluntariado_anterior.si" },
|
||||
{ "value": "no", "label": "form.voluntariado_anterior.no" }
|
||||
]
|
||||
},
|
||||
{ "key": "caso_si", "label": "form.caso_si", "type": "textarea", "required": false, "colspan": 2, "showWhen": { "field": "voluntariado_anterior", "value": "si" } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "form.step3",
|
||||
"fields": [
|
||||
{
|
||||
"key": "areas_colaborar",
|
||||
"label": "form.areas_colaborar",
|
||||
"type": "checkbox",
|
||||
"required": true,
|
||||
"options": [
|
||||
{ "value": "Ayuda Humanitaria", "label": "form.areas.ayuda_humanitaria" },
|
||||
{ "value": "Educacion", "label": "form.areas.educacion" },
|
||||
{ "value": "Desarrollo Comunitario", "label": "form.areas.desarrollo_comunitario" },
|
||||
{ "value": "Liderazgo", "label": "form.areas.liderazgo" },
|
||||
{ "value": "Logistica", "label": "form.areas.logistica" },
|
||||
{ "value": "Organizacion de Eventos", "label": "form.areas.organizacion_eventos" },
|
||||
{ "value": "Comunicacion Institucional", "label": "form.areas.comunicacion_institucional" },
|
||||
{ "value": "Fotografia y Medios", "label": "form.areas.fotografia_medios" },
|
||||
{ "value": "Recaudacion de Fondos", "label": "form.areas.recaudacion_fondos" },
|
||||
{ "value": "Gestion de Proyectos", "label": "form.areas.gestion_proyectos" },
|
||||
{ "value": "Traduccion e Interpretacion", "label": "form.areas.traduccion_interpretacion" },
|
||||
{ "value": "Asesoria Juridica", "label": "form.areas.asesoria_juridica" },
|
||||
{ "value": "Servicios Medicos", "label": "form.areas.servicios_medicos" },
|
||||
{ "value": "Diplomacia Publica", "label": "form.areas.diplomacia_publica" },
|
||||
{ "value": "Administracion", "label": "form.areas.administracion" },
|
||||
{ "value": "Tecnologia e Innovacion", "label": "form.areas.tecnologia_innovacion" },
|
||||
{ "value": "Otra", "label": "form.areas.otra" }
|
||||
]
|
||||
},
|
||||
{ "key": "areas_otra", "label": "form.areas_otra", "type": "text", "required": false, "showWhen": { "field": "areas_colaborar", "value": "Otra" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,441 @@
|
|||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { Icon } from "@iconify/vue";
|
||||
import { createTranslator } from "../../i18n";
|
||||
|
||||
const props = defineProps({
|
||||
locale: String,
|
||||
formConfigUrl: { type: String, default: "/forms/formulario-inscripcion.json" }
|
||||
});
|
||||
|
||||
const tl = createTranslator(props.locale);
|
||||
|
||||
const config = ref(null);
|
||||
const configError = ref(false);
|
||||
const currentStep = ref(0);
|
||||
const formData = reactive({});
|
||||
const isSubmitting = ref(false);
|
||||
const isSuccess = ref(false);
|
||||
const isError = ref(false);
|
||||
const errorMsg = ref("");
|
||||
|
||||
const totalSteps = computed(() => config.value?.steps?.length ?? 0);
|
||||
const currentStepData = computed(() => config.value?.steps?.[currentStep.value] ?? null);
|
||||
|
||||
const isFirstStep = computed(() => currentStep.value === 0);
|
||||
const isLastStep = computed(() => currentStep.value === totalSteps.value - 1);
|
||||
|
||||
const stepErrors = reactive({});
|
||||
|
||||
const fetchConfig = async () => {
|
||||
try {
|
||||
const res = await fetch(props.formConfigUrl);
|
||||
if (!res.ok) throw new Error("Failed to load form config");
|
||||
const json = await res.json();
|
||||
config.value = json;
|
||||
json.steps.forEach((step, si) => {
|
||||
step.fields.forEach((field) => {
|
||||
if (field.type === "checkbox") {
|
||||
formData[field.key] = [];
|
||||
} else {
|
||||
formData[field.key] = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
configError.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(fetchConfig);
|
||||
|
||||
const validateStep = (stepIndex) => {
|
||||
const step = config.value?.steps?.[stepIndex];
|
||||
if (!step) return true;
|
||||
let valid = true;
|
||||
step.fields.forEach((field) => {
|
||||
const val = formData[field.key];
|
||||
if (field.required) {
|
||||
if (field.type === "checkbox") {
|
||||
if (!val || val.length === 0) {
|
||||
stepErrors[field.key] = true;
|
||||
valid = false;
|
||||
} else {
|
||||
stepErrors[field.key] = false;
|
||||
}
|
||||
} else if (!val || val.trim() === "") {
|
||||
stepErrors[field.key] = true;
|
||||
valid = false;
|
||||
} else {
|
||||
stepErrors[field.key] = false;
|
||||
}
|
||||
}
|
||||
if (field.type === "email" && val && val.trim() !== "") {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(val.trim())) {
|
||||
stepErrors[field.key] = true;
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (field.type === "checkbox" && field.levels && Array.isArray(val)) {
|
||||
val.forEach((optVal) => {
|
||||
if (!formData[`${field.key}_nivel_${optVal}`]) {
|
||||
stepErrors[`${field.key}_nivel_${optVal}`] = true;
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return valid;
|
||||
};
|
||||
|
||||
const nextStep = () => {
|
||||
if (!validateStep(currentStep.value)) return;
|
||||
if (currentStep.value < totalSteps.value - 1) {
|
||||
currentStep.value++;
|
||||
}
|
||||
};
|
||||
|
||||
const prevStep = () => {
|
||||
if (currentStep.value > 0) {
|
||||
currentStep.value--;
|
||||
}
|
||||
};
|
||||
|
||||
const goToStep = (index) => {
|
||||
if (index < currentStep.value) {
|
||||
currentStep.value = index;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (!validateStep(i)) return;
|
||||
}
|
||||
currentStep.value = index;
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (key, value, checked) => {
|
||||
if (checked) {
|
||||
formData[key].push(value);
|
||||
} else {
|
||||
formData[key] = formData[key].filter((v) => v !== value);
|
||||
delete formData[`${key}_nivel_${value}`];
|
||||
}
|
||||
stepErrors[key] = false;
|
||||
};
|
||||
|
||||
const buildPayload = () => {
|
||||
const responses = [];
|
||||
config.value.steps.forEach((step, si) => {
|
||||
step.fields.forEach((field) => {
|
||||
const value = formData[field.key];
|
||||
if (value !== "" && !(Array.isArray(value) && value.length === 0)) {
|
||||
responses.push({
|
||||
key: field.key,
|
||||
value: value,
|
||||
section_id: `step_${si}`
|
||||
});
|
||||
if (field.levels && Array.isArray(value)) {
|
||||
value.forEach((optVal) => {
|
||||
const levelKey = `${field.key}_nivel_${optVal}`;
|
||||
const levelVal = formData[levelKey];
|
||||
if (levelVal) {
|
||||
responses.push({
|
||||
key: levelKey,
|
||||
value: levelVal,
|
||||
section_id: `step_${si}`
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return { formData: { ...formData }, responses };
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
for (let i = 0; i < totalSteps.value; i++) {
|
||||
if (!validateStep(i)) {
|
||||
currentStep.value = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
isSuccess.value = false;
|
||||
isError.value = false;
|
||||
errorMsg.value = "";
|
||||
|
||||
try {
|
||||
const payload = buildPayload();
|
||||
const response = await fetch(config.value.action, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
if (!response.ok) throw new Error("Error en el servidor");
|
||||
isSuccess.value = true;
|
||||
setTimeout(() => { isSuccess.value = false; }, 4000);
|
||||
} catch (error) {
|
||||
isError.value = true;
|
||||
errorMsg.value = error.message || "Error al enviar el formulario";
|
||||
setTimeout(() => { isError.value = false; }, 4000);
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const getFieldError = (key) => stepErrors[key] || false;
|
||||
|
||||
const findField = (key) => {
|
||||
if (!config.value) return null;
|
||||
for (const step of config.value.steps) {
|
||||
const found = step.fields.find((f) => f.key === key);
|
||||
if (found) return found;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const shouldShow = (field) => {
|
||||
if (!field.showWhen) return true;
|
||||
const sourceField = findField(field.showWhen.field);
|
||||
const value = formData[field.showWhen.field];
|
||||
if (!value) return false;
|
||||
if (sourceField?.type === "checkbox") {
|
||||
return Array.isArray(value) && value.includes(field.showWhen.value);
|
||||
}
|
||||
return value === field.showWhen.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="configError" class="text-center py-12 text-red-500">
|
||||
{{ tl("form.config_error") }}
|
||||
</div>
|
||||
|
||||
<div v-else-if="!config" class="flex justify-center py-12">
|
||||
<span class="w-8 h-8 border-2 border-[#22523F] border-t-transparent rounded-full animate-spin"></span>
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full max-w-2xl mx-auto">
|
||||
<div class="flex items-center justify-center gap-2 mb-8">
|
||||
<template v-for="(step, index) in config.steps" :key="index">
|
||||
<button
|
||||
type="button"
|
||||
@click="goToStep(index)"
|
||||
class="flex items-center gap-1 text-sm transition-colors"
|
||||
:class="index === currentStep ? 'text-[#22523F] font-bold' : 'text-gray-400 hover:text-[#22523F]'"
|
||||
>
|
||||
<span
|
||||
class="flex items-center justify-center w-8 h-8 rounded-full text-xs font-bold border-2 transition-colors"
|
||||
:class="index === currentStep
|
||||
? 'bg-[#22523F] text-white border-[#22523F]'
|
||||
: index < currentStep
|
||||
? 'bg-[#CBA16A] text-white border-[#CBA16A]'
|
||||
: 'border-gray-300 text-gray-400'"
|
||||
>
|
||||
{{ index < currentStep ? '✓' : index + 1 }}
|
||||
</span>
|
||||
<span class="hidden sm:inline">{{ tl(step.label) }}</span>
|
||||
</button>
|
||||
<span v-if="index < config.steps.length - 1" class="w-8 h-px bg-gray-300"></span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="handleSubmit" class="bg-[#EBE6D2] p-8 rounded-none">
|
||||
<h3 class="text-xl font-bold text-[#22523F] mb-6 text-center">
|
||||
{{ tl(currentStepData.label) }}
|
||||
</h3>
|
||||
|
||||
<div class="grid gap-5" :class="{ 'grid-cols-1': !currentStepData.column || currentStepData.column === 1, 'md:grid-cols-2': currentStepData.column === 2, 'md:grid-cols-3': currentStepData.column === 3 }">
|
||||
<template v-for="field in currentStepData.fields" :key="field.key">
|
||||
<div v-if="shouldShow(field)" class="form-control" :class="{ 'md:col-span-2': field.colspan === 2 && currentStepData.column > 1, 'md:col-span-3': field.colspan === 3 && currentStepData.column > 1 }">
|
||||
<label class="label">
|
||||
<span class="label-text text-[#22523F] font-medium">
|
||||
{{ tl(field.label) }}
|
||||
<span v-if="field.required" class="text-red-500 ml-1">*</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<input
|
||||
v-if="field.type === 'text'"
|
||||
v-model="formData[field.key]"
|
||||
type="text"
|
||||
:placeholder="tl(field.label)"
|
||||
class="input input-bordered w-full rounded-none bg-white border-gray-300 focus:border-[#22523F]"
|
||||
:class="{ 'border-red-500': getFieldError(field.key) }"
|
||||
@input="stepErrors[field.key] = false"
|
||||
/>
|
||||
|
||||
<input
|
||||
v-else-if="field.type === 'email'"
|
||||
v-model="formData[field.key]"
|
||||
type="email"
|
||||
placeholder="email@ejemplo.com"
|
||||
class="input input-bordered w-full rounded-none bg-white border-gray-300 focus:border-[#22523F]"
|
||||
:class="{ 'border-red-500': getFieldError(field.key) }"
|
||||
@input="stepErrors[field.key] = false"
|
||||
/>
|
||||
|
||||
<input
|
||||
v-else-if="field.type === 'phone'"
|
||||
v-model="formData[field.key]"
|
||||
type="tel"
|
||||
:placeholder="tl(field.label)"
|
||||
class="input input-bordered w-full rounded-none bg-white border-gray-300 focus:border-[#22523F]"
|
||||
:class="{ 'border-red-500': getFieldError(field.key) }"
|
||||
@input="stepErrors[field.key] = false"
|
||||
/>
|
||||
|
||||
<input
|
||||
v-else-if="field.type === 'date'"
|
||||
v-model="formData[field.key]"
|
||||
type="date"
|
||||
class="input input-bordered w-full rounded-none bg-white border-gray-300 focus:border-[#22523F]"
|
||||
:class="{ 'border-red-500': getFieldError(field.key) }"
|
||||
@change="stepErrors[field.key] = false"
|
||||
/>
|
||||
|
||||
<textarea
|
||||
v-else-if="field.type === 'textarea'"
|
||||
v-model="formData[field.key]"
|
||||
:placeholder="tl(field.label)"
|
||||
rows="4"
|
||||
class="textarea textarea-bordered w-full rounded-none bg-white border-gray-300 focus:border-[#22523F] resize-none"
|
||||
:class="{ 'border-red-500': getFieldError(field.key) }"
|
||||
@input="stepErrors[field.key] = false"
|
||||
></textarea>
|
||||
|
||||
<div v-else-if="field.type === 'radio'" class="flex flex-col gap-2">
|
||||
<label
|
||||
v-for="opt in field.options"
|
||||
:key="opt.value"
|
||||
class="flex flex-row items-center gap-2 cursor-pointer"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
:name="field.key"
|
||||
:value="opt.value"
|
||||
v-model="formData[field.key]"
|
||||
class="radio radio-md radio-[#22523F]"
|
||||
@change="stepErrors[field.key] = false"
|
||||
/>
|
||||
<span class="text-[#22523F]">{{ tl(opt.label) }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-else-if="field.type === 'checkbox'" class="flex flex-col gap-2">
|
||||
<div
|
||||
v-for="opt in field.options"
|
||||
:key="opt.value"
|
||||
class="flex flex-col gap-1"
|
||||
>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="opt.value"
|
||||
class="checkbox checkbox-sm checkbox-[#22523F]"
|
||||
:checked="formData[field.key]?.includes(opt.value)"
|
||||
@change="handleCheckboxChange(field.key, opt.value, $event.target.checked)"
|
||||
/>
|
||||
<span class="text-[#22523F]">{{ tl(opt.label) }}</span>
|
||||
</label>
|
||||
<div
|
||||
v-if="formData[field.key]?.includes(opt.value) && field.levels"
|
||||
class="flex gap-3 ml-7"
|
||||
>
|
||||
<label
|
||||
v-for="lvl in field.levels.options"
|
||||
:key="lvl.value"
|
||||
class="flex items-center gap-1 cursor-pointer text-sm"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
:name="`${field.key}_nivel_${opt.value}`"
|
||||
:value="lvl.value"
|
||||
v-model="formData[`${field.key}_nivel_${opt.value}`]"
|
||||
class="radio radio-sm radio-[#22523F]"
|
||||
/>
|
||||
<span class="text-[#22523F]">{{ tl(lvl.label) }}</span>
|
||||
</label>
|
||||
<span
|
||||
v-if="getFieldError(`${field.key}_nivel_${opt.value}`)"
|
||||
class="text-red-500 text-xs"
|
||||
>
|
||||
{{ tl("form.required") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="getFieldError(field.key)"
|
||||
class="text-red-500 text-xs mt-1"
|
||||
>
|
||||
{{ tl("form.required") }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="isError && errorMsg" class="text-red-600 text-sm text-center mt-4">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between mt-8">
|
||||
<button
|
||||
v-if="!isFirstStep"
|
||||
type="button"
|
||||
@click="prevStep"
|
||||
class="btn rounded-none border-[#22523F] text-[#22523F] hover:bg-[#22523F] hover:text-white"
|
||||
>
|
||||
<Icon icon="ph:arrow-left" class="text-lg" />
|
||||
{{ tl("form.back") }}
|
||||
</button>
|
||||
<div v-else></div>
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
v-if="!isLastStep"
|
||||
type="button"
|
||||
@click="nextStep"
|
||||
class="btn rounded-none bg-[#22523F] text-white hover:bg-[#1a3d2f]"
|
||||
>
|
||||
{{ tl("form.next") }}
|
||||
<Icon icon="ph:arrow-right" class="text-lg" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-else
|
||||
type="submit"
|
||||
:disabled="isSubmitting"
|
||||
class="btn rounded-none px-8 transition-colors"
|
||||
:class="isSubmitting
|
||||
? 'bg-gray-400 text-white cursor-not-allowed'
|
||||
: isSuccess
|
||||
? 'bg-green-600 text-white'
|
||||
: isError
|
||||
? 'bg-red-600 text-white'
|
||||
: 'bg-[#22523F] text-white hover:bg-[#1a3d2f]'"
|
||||
>
|
||||
<span v-if="isSubmitting" class="flex items-center gap-2">
|
||||
<span class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></span>
|
||||
{{ tl("form.sending") }}
|
||||
</span>
|
||||
<span v-else-if="isSuccess" class="flex items-center gap-2">
|
||||
<Icon icon="ph:check-circle" class="text-lg" />
|
||||
{{ tl("form.success") }}
|
||||
</span>
|
||||
<span v-else-if="isError" class="flex items-center gap-2">
|
||||
<Icon icon="ph:x-circle" class="text-lg" />
|
||||
{{ tl("form.error") }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ tl(config.submit_label) }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -200,5 +200,71 @@
|
|||
"footer.form.name": "Nombre y Apellido",
|
||||
"footer.form.mesagge": "Escriba su mensaje",
|
||||
"footer.form.button": "Enviar",
|
||||
"footer.reserved": "©2026. Todos los Derechos Reservados. Centro del Reino de Paz y Justicia"
|
||||
"footer.reserved": "©2026. Todos los Derechos Reservados. Centro del Reino de Paz y Justicia",
|
||||
"form.page_title": "SOLICITUD DE INSCRIPCIÓN COMO VOLUNTARIO",
|
||||
"form.page_desc": "Completá tus datos para formar parte del Centro del Reino de Paz y Justicia.",
|
||||
"form.step1": "Datos Personales",
|
||||
"form.step2": "Formación Academica y Experiencia",
|
||||
"form.nombre": "Nombre",
|
||||
"form.apellido": "Apellido",
|
||||
"form.documentos": "Documento de Identidad / Pasaporte",
|
||||
"form.fecha_nacimiento": "Fecha de Nacimiento",
|
||||
"form.nacionalidad": "Nacionalidad",
|
||||
"form.sexo": "Sexo",
|
||||
"form.sexo.m": "Masculino",
|
||||
"form.sexo.f": "Femenino",
|
||||
"form.sexo.o": "Otro",
|
||||
"form.direccion": "Dirección Completa",
|
||||
"form.ciudad": "Ciudad",
|
||||
"form.estado": "Estado / Provincia",
|
||||
"form.pais": "País",
|
||||
"form.codigo_postal": "Código Postal",
|
||||
"form.mobile": "Teléfono Móvil",
|
||||
"form.whatsapp": "WhatsApp",
|
||||
"form.correo": "Correo Electrónico",
|
||||
"form.profesion": "Profesión u ocupación",
|
||||
"form.lugar_trabajo_actual": "Lugar de trabajo actual",
|
||||
"form.nivel_academico": "Nivel académico",
|
||||
"form.idioma": "Idiomas que domina",
|
||||
"form.idioma.esp": "Español",
|
||||
"form.idioma.ing": "Inglés",
|
||||
"form.idioma.heb": "Hebreo",
|
||||
"form.idioma.port": "Portugués",
|
||||
"form.idioma.otro": "Otro",
|
||||
"form.idioma_otro": "Otro (especificar)",
|
||||
"form.voluntariado_anterior": "¿Ha realizado voluntariado anteriormente?",
|
||||
"form.voluntariado_anterior.si": "Sí",
|
||||
"form.voluntariado_anterior.no": "No",
|
||||
"form.caso_si": "En caso afirmativo, indique dónde y en qué funciones",
|
||||
"form.nivel.basico": "Básico",
|
||||
"form.nivel.intermedio": "Intermedio",
|
||||
"form.nivel.avanzado": "Avanzado",
|
||||
"form.step3": "Áreas de Colaboración",
|
||||
"form.areas_colaborar": "Seleccione las áreas en las que desea colaborar",
|
||||
"form.areas.ayuda_humanitaria": "Ayuda Humanitaria",
|
||||
"form.areas.educacion": "Educación",
|
||||
"form.areas.desarrollo_comunitario": "Desarrollo Comunitario",
|
||||
"form.areas.liderazgo": "Liderazgo",
|
||||
"form.areas.logistica": "Logística",
|
||||
"form.areas.organizacion_eventos": "Organización de Eventos",
|
||||
"form.areas.comunicacion_institucional": "Comunicación Institucional",
|
||||
"form.areas.fotografia_medios": "Fotografía y Medios",
|
||||
"form.areas.recaudacion_fondos": "Recaudación de Fondos",
|
||||
"form.areas.gestion_proyectos": "Gestión de Proyectos",
|
||||
"form.areas.traduccion_interpretacion": "Traducción e Interpretación",
|
||||
"form.areas.asesoria_juridica": "Asesoría Jurídica",
|
||||
"form.areas.servicios_medicos": "Servicios Médicos / Primeros Auxilios",
|
||||
"form.areas.diplomacia_publica": "Diplomacia Pública",
|
||||
"form.areas.administracion": "Administración",
|
||||
"form.areas.tecnologia_innovacion": "Tecnología e Innovación",
|
||||
"form.areas.otra": "Otra",
|
||||
"form.areas_otra": "Otra (especificar)",
|
||||
"form.submit": "Enviar Formulario",
|
||||
"form.next": "Siguiente",
|
||||
"form.back": "Anterior",
|
||||
"form.sending": "Enviando...",
|
||||
"form.success": "¡Enviado con éxito!",
|
||||
"form.error": "Error al enviar",
|
||||
"form.required": "Este campo es obligatorio",
|
||||
"form.config_error": "Error al cargar el formulario. Intentá de nuevo más tarde."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
import MainLayout from "@/layouts/MainLayout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import FooterSection from "@/components/section/FooterSection.astro";
|
||||
import DynamicForm from "@/components/forms/DynamicForm.vue";
|
||||
import { createTranslator } from "@/i18n";
|
||||
|
||||
const tl = createTranslator(Astro.currentLocale);
|
||||
---
|
||||
|
||||
<MainLayout title={tl("form.page_title")}>
|
||||
<div class="pt-16 relative container mx-auto">
|
||||
<Header />
|
||||
</div>
|
||||
<main class="min-h-screen bg-white py-16 px-4">
|
||||
<div class="container mx-auto">
|
||||
<h1 class="text-3xl lg:text-4xl font-bold text-[#22523F] text-center mb-4">
|
||||
{tl("form.page_title")}
|
||||
</h1>
|
||||
<!-- <p class="text-gray-600 text-center mb-10 max-w-lg mx-auto">
|
||||
{tl("form.page_desc")}
|
||||
</p> -->
|
||||
<DynamicForm client:load locale={Astro.currentLocale} />
|
||||
</div>
|
||||
</main>
|
||||
<FooterSection />
|
||||
</MainLayout>
|
||||
Loading…
Reference in New Issue