155 lines
5.3 KiB
Vue
155 lines
5.3 KiB
Vue
<script setup>
|
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
import { Head, useForm } from '@inertiajs/vue3'
|
|
import { ArrowLeftIcon } from '@heroicons/vue/24/outline'
|
|
import { ExclamationTriangleIcon } from '@heroicons/vue/20/solid'
|
|
import AppLayout from '@/Layouts/AppLayout.vue'
|
|
import AppButton from '@/Components/AppButton.vue'
|
|
import RadioButtonGroup from '@/Components/RadioButtonGroup.vue'
|
|
|
|
defineOptions({ layout: AppLayout })
|
|
|
|
const MAX_MAYBE_ANSWERS = 8
|
|
|
|
const props = defineProps({
|
|
screening: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
questions: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
})
|
|
|
|
// Initialize form with empty answers for all questions
|
|
const initialAnswers = {}
|
|
props.questions.forEach((_, index) => {
|
|
initialAnswers[index + 1] = null
|
|
})
|
|
|
|
const form = useForm({
|
|
answers: initialAnswers,
|
|
})
|
|
|
|
const handleSubmit = () => {
|
|
form.put(`/screening/${props.screening.id}`)
|
|
}
|
|
|
|
const currentScore = computed(() => {
|
|
return Object.values(form.answers).reduce((score, value) => {
|
|
if (value === 'yes') return score + 1
|
|
if (value === 'unknown') return score + 0.5
|
|
return score
|
|
}, 0)
|
|
})
|
|
|
|
watch(currentScore, (score) => {
|
|
console.log('Current screening score:', score)
|
|
})
|
|
|
|
const allAnswered = computed(() => {
|
|
return Object.values(form.answers).every(v => v !== null)
|
|
})
|
|
|
|
const unknownCount = computed(() => {
|
|
return Object.values(form.answers).filter(v => v === 'unknown').length
|
|
})
|
|
|
|
const maybeLimitReached = computed(() => {
|
|
return unknownCount.value >= MAX_MAYBE_ANSWERS
|
|
})
|
|
|
|
const bottomBackButtonRef = ref(null)
|
|
const showStickyBack = ref(false)
|
|
|
|
let observer = null
|
|
|
|
onMounted(() => {
|
|
if (!bottomBackButtonRef.value) return
|
|
observer = new IntersectionObserver(
|
|
([entry]) => { showStickyBack.value = !entry.isIntersecting },
|
|
{ threshold: 0 }
|
|
)
|
|
observer.observe(bottomBackButtonRef.value.$el || bottomBackButtonRef.value)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
observer?.disconnect()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Head title="Pre-Screening Questions" />
|
|
|
|
<div class="max-w-4xl mx-auto px-4 py-8">
|
|
<!-- Sticky back button -->
|
|
<Transition
|
|
enter-active-class="transition-all duration-200 ease-out"
|
|
enter-from-class="opacity-0 translate-x-4"
|
|
enter-to-class="opacity-100 translate-x-0"
|
|
leave-active-class="transition-all duration-150 ease-in"
|
|
leave-from-class="opacity-100 translate-x-0"
|
|
leave-to-class="opacity-0 translate-x-4"
|
|
>
|
|
<div v-if="showStickyBack" class="fixed top-[88px] right-6 z-40">
|
|
<AppButton variant="ghost" size="lg" href="/" data-cy="sticky-back">
|
|
<ArrowLeftIcon class="h-5 w-5" />
|
|
Back
|
|
</AppButton>
|
|
</div>
|
|
</Transition>
|
|
|
|
<h1 class="text-3xl font-bold text-white mb-2">Pre-Screening Questions</h1>
|
|
<p class="text-gray-400 mb-8">Please answer all questions to proceed.</p>
|
|
|
|
<p v-if="maybeLimitReached" class="text-amber-400/80 text-sm mb-6 flex items-center gap-2">
|
|
<ExclamationTriangleIcon class="h-4 w-4 shrink-0" />
|
|
You've used all {{ MAX_MAYBE_ANSWERS }} "I don't know" answers. Please choose Yes or No for the remaining questions.
|
|
</p>
|
|
|
|
<div class="space-y-4 mb-8">
|
|
<div
|
|
v-for="(question, index) in questions"
|
|
:key="index"
|
|
class="rounded-lg p-5"
|
|
:data-cy="`screening-answer-${index + 1}`"
|
|
>
|
|
<div class="flex items-start gap-4">
|
|
<span class="text-gray-400 font-mono text-sm mt-1 shrink-0">{{ index + 1 }}.</span>
|
|
<div class="flex-1">
|
|
<p class="text-white mb-3">{{ question }}</p>
|
|
<RadioButtonGroup
|
|
v-model="form.answers[index + 1]"
|
|
:name="`question-${index + 1}`"
|
|
:options="[
|
|
{ value: 'yes', label: 'Yes' },
|
|
{ value: 'unknown', label: 'I don\'t know' },
|
|
{ value: 'no', label: 'No' },
|
|
]"
|
|
:disabled-options="maybeLimitReached && form.answers[index + 1] !== 'unknown' ? ['unknown'] : []"
|
|
disabled-tooltip="You can only select "I don't know" for a maximum of 8 questions"
|
|
/>
|
|
<p v-if="form.errors[`answers.${index + 1}`]" class="text-red-500 text-sm mt-1">
|
|
{{ form.errors[`answers.${index + 1}`] }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-12 pt-8 border-t border-white/[0.06]">
|
|
<div class="flex justify-between items-center">
|
|
<AppButton ref="bottomBackButtonRef" variant="ghost" size="lg" href="/" data-cy="back-to-landing">
|
|
<ArrowLeftIcon class="h-5 w-5" />
|
|
Back
|
|
</AppButton>
|
|
|
|
<AppButton size="lg" @click="handleSubmit" :loading="form.processing" :disabled="!allAnswered || form.processing" data-cy="submit-screening">
|
|
Submit
|
|
</AppButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|