Skip to content

Commit

Permalink
Desafios and Iniciativas forms
Browse files Browse the repository at this point in the history
  • Loading branch information
guillecro committed Dec 19, 2024
1 parent 433ed97 commit b593a33
Show file tree
Hide file tree
Showing 7 changed files with 683 additions and 25 deletions.
219 changes: 219 additions & 0 deletions components/desafios/Form.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
<script setup>
import {
object as YupObject,
string as YupString,
boolean as YupBoolean,
number as YupNumber,
} from 'yup'
const { $api } = useNuxtApp()
const isLoading = ref(false)
const dimensions = ref([])
const subdivisions = ref([])
const submitSuccess = ref(false)
const submitError = ref(false)
const submitLoading = ref(false)
const schema = YupObject({
dimensionId: YupNumber().required('Este campo es requerido'),
cityId: YupNumber().required('Este campo es requerido'),
subdivisionId: YupNumber().required('Este campo es requerido'),
needsAndChallenges: YupString().max(500, 'El máximo es hasta 500 caracteres').required('Este campo es requerido'),
proposal: YupString().max(500, 'El máximo es hasta 500 caracteres').required('Este campo es requerido'),
inWords: YupString().matches(/^\w+(\s\w+)?$/, { excludeEmptyString: true, message: 'Solo se permite una o dos palabras' }).required('Este campo es requerido'),
acceptsTerms: YupBoolean().oneOf([true], 'Debes aceptar los términos y condiciones')
})
const state = reactive({
dimensionId: null,
cityId: null,
subdivisionId: null,
needsAndChallenges: null,
proposal: null,
inWords: null,
acceptsTerms: false
})
onMounted(() => {
console.log('Mounted')
getInitData()
})
const getInitData = async () => {
isLoading.value = true
try {
const [dimensionsData, subdivisionsData] = await Promise.all([
$api('/utils/dimensions'),
$api('/utils/subdivisions')
])
dimensions.value = dimensionsData
subdivisions.value = subdivisionsData
} catch (error) {
console.error(error)
} finally {
isLoading.value = false
}
}
const citiesOptions = computed(() => {
const cities = []
if(!subdivisions.value) return cities
if(!subdivisions.value.length) return cities
subdivisions.value.forEach(subdivision => {
// check if city is already in the list
const city = cities.find(city => city.id === subdivision.city.id)
if(!city) {
cities.push(subdivision.city)
}
})
return cities
})
const subdivisionsOptions = computed(() => {
if(!state.cityId) return []
// filtered subdivisions
const filteredSubdivisions = subdivisions.value.filter(subdivision => {
return subdivision.city.id === state.cityId
})
return filteredSubdivisions
})
const selectedLabelSubdivisions = computed(() => {
if(state.cityId === 1) return "Cali: Selecciona tu corregimiento"
if(state.cityId === 2) return "Bogota: Selecciona tu comuna"
return '-'
})
watch(() => state.cityId, (newValue, oldValue) => {
if(newValue != oldValue) {
state.subdivisionId = null
}
})
const handleSubmit = async () => {
const payload = {
dimensionId: state.dimensionId,
subdivisionId: state.subdivisionId,
needsAndChallenges: state.needsAndChallenges,
proposal: state.proposal,
inWords: state.inWords,
}
submitLoading.value = true
try {
await $api('/challenges', {
method: 'POST',
body: JSON.stringify(payload)
})
submitSuccess.value = true
submitError.value = false
} catch (error) {
submitError.value = true
submitSuccess.value = false
console.error(error)
} finally {
submitLoading.value = false
}
}
const selectDimension = (dimension) => {
state.dimensionId = dimension.id
}
</script>

<template>
<UCard v-if="isLoading">
<UProgress color="pumpkin" />
</UCard>
<UCard v-else>
<UAlert
v-if="submitError"
icon="i-heroicons-exclamation-triangle"
color="red"
variant="subtle"
:close-button="{
icon: 'i-heroicons-x-mark-20-solid',
padded: false,
color: 'red',
variant: 'link',
onClick: () => submitError = false
}"
class="mb-5"
title="Ocurrion un error">
<template #description>
<p>No hemos podido guardar tu reporte, por favor intentalo de nuevo o contacta a soporte</p>
</template>
</UAlert>
<div v-if="!submitSuccess">
<p class="text-2xl text-pumpkin-500 font-bold mb-5">REPORTA DESAFIOS DE LA JUVENTUD</p>
<UForm :state="state" :schema="schema" class="space-y-4" @submit="handleSubmit">
<!-- <UFormGroup class="" label="Linea temática" name="dimensionId" required>
<USelect v-model.number="state.dimensionId" :options="dimensions" value-attribute="id" option-attribute="name" :disabled="submitLoading"/>
</UFormGroup> -->
<UFormGroup name="dimensionId" label="Eje tematico del desafio" required>
<div class="flex flex-wrap gap-2 pt-1">
<UBadge
v-for="dimension in dimensions"
:key="`dimension-${dimension.id}`"
:color="state.dimensionId == dimension.id ? 'mindaro' : 'white'"
:variant="state.dimensionId == dimension.id ? 'solid' : 'outline'"
class="cursor-pointer px-3"
:ui="{ rounded: 'rounded-full' }"
@click="selectDimension(dimension)">
{{ dimension.name }}
</UBadge>
</div>
</UFormGroup>
<UFormGroup class="" label="Ciudad" name="cityId" required>
<USelect v-model.number="state.cityId" :options="citiesOptions" value-attribute="id" option-attribute="name" :disabled="submitLoading"/>
</UFormGroup>
<UFormGroup v-if="state.cityId" class="" :label="selectedLabelSubdivisions" name="subdivisionId" required>
<USelect v-model.number="state.subdivisionId" :options="subdivisionsOptions" value-attribute="id" option-attribute="name" :disabled="!state.cityId || submitLoading"/>
</UFormGroup>
<UFormGroup class="" label="Necesidades y desafíos" name="needsAndChallenges" required>
<template #description>
Comparte tu necesidad y/o desafio <i class="text-pumpkin">Max. 500 caracteres.</i>
</template>
<UTextarea v-model="state.needsAndChallenges" :disabled="submitLoading" />
</UFormGroup>
<UFormGroup class="" label="Propuesta" name="proposal" required>
<template #description>
¿Tienes alguna propuesta frente a esta situación? <i class="text-pumpkin">Max. 500 caracteres.</i>
</template>
<template #help>
<span v-if="state.proposal" class="text-xs">Caracteres: {{ state.proposal.length }}</span>
</template>
<UTextarea v-model="state.proposal" placeholder="Ingresá tu propuesta aquí" :disabled="submitLoading"/>
</UFormGroup>
<UFormGroup class="" label="En pocas palabras" name="inWords" required>
<template #description>
¿Cómo describirías tu propuesta en pocas palabras?
</template>
<UInput v-model="state.inWords" :disabled="submitLoading" />
</UFormGroup>
<UFormGroup class="flex items-center" name="acceptsTerms" required>
<UCheckbox v-model="state.acceptsTerms" :disabled="submitLoading">
<template #label>
Acepto los <ULink to="/acerca-de/terminos-y-condiciones" class="text-pumpkin-500">términos y condiciones</ULink>
</template>
</UCheckbox>
</UFormGroup>
<UDivider />
<UButton color="pumpkin" block size="xl" type="submit" :loading="submitLoading">Enviar</UButton>
</UForm>
</div>
<div v-else class="text-center">
<UIcon name="i-heroicons-check-circle" class="text-6xl text-green-500" />
<p class="text-green-500 font-oswald uppercase text-3xl mb-3">¡GRACIAS POR REPORTAR TU DESAFIO!</p>
<p>Tu reporte ha sido guardado en nuestra base de datos</p>
</div>

</UCard>
</template>
158 changes: 158 additions & 0 deletions components/desafios/ModalForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<script setup>
import {
object as YupObject,
string as YupString,
boolean as YupBoolean,
number as YupNumber,
} from 'yup'
const { $api } = useNuxtApp()
const isLoading = ref(false)
const dimensions = ref([])
const subdivisions = ref([])
const schema = YupObject({
dimension: YupNumber().required('Este campo es requerido'),
city: YupNumber().required('Este campo es requerido'),
subdivision: YupNumber().required('Este campo es requerido'),
needsAndChallenges: YupString().max(500).required('Este campo es requerido'),
proposal: YupString().max(500).required('Este campo es requerido'),
inWords: YupString().matches(/^\w+(\s\w+)?$/, { excludeEmptyString: true }).required('Este campo es requerido'),
acceptsTerms: YupBoolean().oneOf([true], 'Debes aceptar los términos y condiciones')
})
const state = reactive({
dimension: null,
city: null,
subdivision: null,
needsAndChallenges: null,
proposal: null,
inWords: null,
acceptsTerms: false
})
onMounted(() => {
console.log('Mounted')
getInitData()
})
const getInitData = async () => {
isLoading.value = true
try {
const [dimensionsData, subdivisionsData] = await Promise.all([
$api('/utils/dimensions'),
$api('/utils/subdivisions')
])
dimensions.value = dimensionsData
subdivisions.value = subdivisionsData
} catch (error) {
console.error(error)
} finally {
isLoading.value = false
}
}
const citiesOptions = computed(() => {
const cities = []
if(!subdivisions.value) return cities
if(!subdivisions.value.length) return cities
subdivisions.value.forEach(subdivision => {
// check if city is already in the list
const city = cities.find(city => city.id === subdivision.city.id)
if(!city) {
cities.push(subdivision.city)
}
})
return cities
})
const subdivisionsOptions = computed(() => {
if(!state.city) return []
// filtered subdivisions
const filteredSubdivisions = subdivisions.value.filter(subdivision => {
return subdivision.city.id === state.city
})
return filteredSubdivisions
})
const selectedLabelSubdivisions = computed(() => {
if(state.city === 1) return "Cali: Selecciona tu corregimiento"
if(state.city === 2) return "Bogota: Selecciona tu comuna"
return '-'
})
watch(() => state.city, (newValue, oldValue) => {
if(newValue != oldValue) {
state.subdivision = null
}
})
const handleSubmit = async () => {
try {
await schema.validate(state, { abortEarly: false })
console.log('Form is valid')
} catch (error) {
console.error(error)
}
}
</script>

<template>
<UModal>
<UCard v-if="isLoading">
<UProgress color="pumpkin" />
</UCard>
<UCard>
<p class="text-2xl text-pumpkin-500 font-bold mb-5">REPORTA DESAFIOS DE LA JUVENTUD</p>
<UForm :state="state" :schema="schema" class="space-y-4" @submit="handleSubmit">
<!-- <UFormGroup class="" label="Linea temática" name="dimension" required>
<USelect v-model="state.dimension" :options="dimensions" value-attribute="id" option-attribute="name"/>
</UFormGroup> -->
<UFormGroup class="" label="Ciudad" name="city" required>
<USelect v-model.number="state.city" :options="citiesOptions" value-attribute="id" option-attribute="name"/>
</UFormGroup>
<UFormGroup v-if="state.city" class="" :label="selectedLabelSubdivisions" name="subdivision" required>
<USelect v-model.number="state.subdivision" :options="subdivisionsOptions" value-attribute="id" option-attribute="name" :disabled="!state.city"/>
</UFormGroup>
<UFormGroup class="" label="Necesidades y desafíos" name="needsAndChallenges" required>
<template #description>
Comparte tu necesidad y/o desafio <i class="text-pumpkin">Max. 500 caracteres.</i>
</template>
<UTextarea v-model="state.needsAndChallenges" />
</UFormGroup>
<UFormGroup class="" label="Propuesta" name="proposal" required>
<template #description>
¿Tienes alguna propuesta frente a esta situación? <i class="text-pumpkin">Max. 500 caracteres.</i>
</template>
<template #help>
<span v-if="state.proposal" class="text-xs">Caracteres: {{ state.proposal.length }}</span>
</template>
<UTextarea v-model="state.proposal" placeholder="Ingresá tu propuesta aquí" />
</UFormGroup>
<UFormGroup class="" label="En pocas palabras" name="inWords" required>
<template #description>
¿Cómo describirías tu propuesta en pocas palabras?
</template>
<UInput v-model="state.inWords" />
</UFormGroup>
<UFormGroup class="flex items-center" name="acceptsTerms" required>
<UCheckbox v-model="state.acceptsTerms">
<template #label>
Acepto los <ULink to="/terminos-y-condiciones" class="text-pumpkin-500">términos y condiciones</ULink>
</template>
</UCheckbox>
</UFormGroup>
<UDivider />
<UButton color="pumpkin" block size="xl" type="submit">Enviar</UButton>
</UForm>


</UCard>
</UModal>

</template>
Loading

0 comments on commit b593a33

Please sign in to comment.