wip: init form entry education assessment

import clinical const from sheets

wip: form entry add education assessment

done: checkbox

wip: add select Asesmen Kemampuan dan Kemauan Belajar
This commit is contained in:
Khafid Prayoga
2025-10-21 11:21:06 +07:00
parent 27ab7c2e83
commit 736e951f33
12 changed files with 600 additions and 1 deletions
@@ -0,0 +1,137 @@
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { Form } from '~/components/pub/ui/form'
import * as DE from '~/components/pub/my-ui/doc-entry'
import Separator from '~/components/pub/ui/separator/Separator.vue'
import { BaseSelect, BaseTextarea, CheckboxGeneral, CheckboxSpecial } from './field'
// constant
import {
abilityCode,
willCode,
medObstacleCode,
learnMethodCode,
langClassCode,
translatorSrcCode,
} from '~/lib/clinical.constants'
const props = defineProps<{
schema: any
initialValues?: any
}>()
const formSchema = toTypedSchema(props.schema)
const formRef = ref()
defineExpose({
validate: () => formRef.value?.validate(),
resetForm: () => formRef.value?.resetForm(),
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
values: computed(() => formRef.value?.values),
})
</script>
<template>
<Form
ref="formRef"
v-slot="{ values }"
as=""
keep-values
:validation-schema="formSchema"
:validate-on-mount="false"
validation-mode="onSubmit"
:initial-values="initialValues ? initialValues : {}"
>
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Kebutuhan Edukasi</p>
<DE.Block
:col-count="4"
:cell-flex="false"
>
<CheckboxGeneral
field-name="generalEducationNeeds"
label="Informasi Umum"
:col-span="2"
/>
<CheckboxSpecial
field-name="specificEducationNeeds"
label="Edukasi Khusus"
:col-span="2"
/>
</DE.Block>
<div class="h-6">
<Separator />
</div>
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Asesmen Kemampuan dan Kemauan Belajar</p>
<DE.Block
:col-count="3"
:cell-flex="false"
>
<BaseSelect
field-name="learningAbility"
label="Kemampuan Belajar"
:items="abilityCode"
/>
<BaseSelect
field-name="learningWillingness"
label="Kemauan Belajar"
:items="willCode"
/>
<BaseSelect
field-name="barrier"
label="Hambatan"
:items="medObstacleCode"
/>
<BaseSelect
field-name="learningMethod"
label="Metode Pembelajaran"
:items="learnMethodCode"
/>
<BaseSelect
field-name="language"
label="Bahasa"
:items="langClassCode"
/>
<BaseSelect
field-name="languageBarrier"
label="Hambatan Bahasa"
:items="translatorSrcCode"
/>
<BaseSelect
field-name="beliefValue"
label="Keyakinan pada Nilai-Nilai yang Dianut"
:items="{
ya: 'IYA',
tidak: 'TIDAK',
}"
/>
</DE.Block>
<div class="h-6">
<Separator />
</div>
<p class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base">Rencana Edukasi</p>
<DE.Block>
<BaseTextarea
field-name="recommendation"
label="Plan A"
/>
<BaseTextarea
field-name="recommendation"
label="Plan B"
/>
<BaseTextarea
field-name="recommendation"
label="Plan Plan Pak Supir"
/>
</DE.Block>
</Form>
</template>
@@ -0,0 +1,64 @@
<script setup lang="ts">
import * as DE from '~/components/pub/my-ui/doc-entry'
import { Checkbox } from '~/components/pub/ui/checkbox'
import type { CheckItem } from '~/lib/utils'
const props = defineProps<{
label: string
fieldName: string
items: CheckItem[]
colSpan?: number
}>()
const { label, fieldName, items } = props
</script>
<template>
<DE.Cell :col-span="colSpan || 1">
<DE.Label :label-for="fieldName">
{{ label }}
</DE.Label>
<DE.Field :id="fieldName">
<FormField
:name="fieldName"
v-slot="{ value, handleChange }"
>
<FormItem>
<div
v-for="item in items"
:key="item.id"
class="ml-1 flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
:id="`cb-${fieldName}-${item.id}`"
:checked="value?.includes(item.id)"
@update:checked="
(checked) => {
const newValue = [...(value || [])]
if (checked) {
if (!newValue.includes(item.id)) newValue.push(item.id)
} else {
const idx = newValue.indexOf(item.id)
if (idx > -1) newValue.splice(idx, 1)
}
handleChange(newValue)
}
"
/>
</FormControl>
<FormLabel
:for="`cb-${fieldName}-${item.id}`"
class="font-normal leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 md:!text-xs 2xl:text-sm"
>
{{ item.label }}
</FormLabel>
</div>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,62 @@
<script setup lang="ts">
import Select from '~/components/pub/my-ui/form/select.vue'
import { cn, mapToComboboxOptList } from '~/lib/utils'
import * as DE from '~/components/pub/my-ui/doc-entry'
const props = defineProps<{
fieldName: string
label: string
items: Record<string, string>
placeholder?: string
class?: string
selectClass?: string
fieldGroupClass?: string
labelClass?: string
isRequired?: boolean
}>()
const { placeholder = 'Pilih', class: containerClass, selectClass, fieldGroupClass, labelClass } = props
const religionOptions = mapToComboboxOptList(props.items)
</script>
<template>
<DE.Cell :class="cn('select-field-group', fieldGroupClass, containerClass)">
<DE.Label
:label-for="fieldName"
:class="cn('select-field-label', labelClass)"
:is-required="isRequired"
>
{{ label }}
</DE.Label>
<DE.Field
:id="fieldName"
:class="cn('select-field-wrapper')"
>
<FormField
v-slot="{ componentField }"
:name="fieldName"
>
<FormItem>
<FormControl>
<Select
:id="fieldName"
v-bind="componentField"
:items="religionOptions"
:placeholder="placeholder"
:preserve-order="false"
:class="
cn(
'text-sm transition-all duration-200 focus:outline-none focus:ring-1 focus:ring-black focus:ring-offset-0',
selectClass,
)
"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,36 @@
<script setup lang="ts">
import * as DE from '~/components/pub/my-ui/doc-entry'
const props = defineProps<{
label: string
fieldName: string
placeholder?: string
colSpan?: number
}>()
const { label, fieldName, placeholder = 'Masukkan catatan' } = props
</script>
<template>
<DE.Cell :col-span="colSpan || 1">
<DE.Label :label-for="fieldName">
{{ label }}
</DE.Label>
<DE.Field :id="fieldName">
<FormField
v-slot="{ componentField }"
:name="fieldName"
>
<FormItem>
<FormControl>
<Textarea
v-bind="componentField"
:placeholder="placeholder"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,23 @@
<script setup lang="ts">
import BaseCheckbox from './base-checkbox.vue'
import { generalEduCode } from '~/lib/clinical.constants'
import { mapToCheckItems } from '~/lib/utils'
interface Props {
label: string
fieldName: string
colSpan?: number
}
defineProps<Props>()
const generalItems = mapToCheckItems(generalEduCode)
</script>
<template>
<BaseCheckbox
:field-name="fieldName"
:label="label"
:col-span="colSpan"
:items="generalItems"
/>
</template>
@@ -0,0 +1,22 @@
<script setup lang="ts">
import BaseCheckbox from './base-checkbox.vue'
import { specialEduCode } from '~/lib/clinical.constants'
import { mapToCheckItems } from '~/lib/utils'
interface Props {
label: string
fieldName: string
colSpan?: number
}
defineProps<Props>()
const specialItems = mapToCheckItems(specialEduCode)
</script>
<template>
<BaseCheckbox
:field-name="fieldName"
:label="label"
:col-span="colSpan"
:items="specialItems"
/>
</template>
@@ -0,0 +1,5 @@
export { default as BaseSelect } from './base-select.vue'
export { default as BaseTextarea } from './base-textarea.vue'
export { default as CheckboxGeneral } from './checkbox-general.vue'
export { default as CheckboxSpecial } from './checkbox-special.vue'
export { default as SelectAssessmentCode } from './select-assessment-code.vue'
@@ -0,0 +1,36 @@
<script setup lang="ts">
import * as DE from '~/components/pub/my-ui/doc-entry'
const props = defineProps<{
label: string
fieldName: string
placeholder?: string
colSpan?: number
}>()
const { label, fieldName, placeholder = 'Masukkan catatan' } = props
</script>
<template>
<DE.Cell :col-span="colSpan || 1">
<DE.Label :label-for="fieldName">
{{ label }}
</DE.Label>
<DE.Field :id="fieldName">
<FormField
v-slot="{ componentField }"
:name="fieldName"
>
<FormItem>
<FormControl>
<Textarea
v-bind="componentField"
:placeholder="placeholder"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</DE.Field>
</DE.Cell>
</template>
@@ -0,0 +1,72 @@
<script setup lang="ts">
import * as z from 'zod'
import type { ExposedForm } from '~/types/form'
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
const schema = z.object({
generalEducationNeeds: z
.array(z.string(), {
required_error: 'Mohon pilih setidaknya item',
})
.min(1, 'Mohon pilih setidaknya item'),
specificEducationNeeds: z
.array(z.string(), {
required_error: 'Mohon pilih setidaknya item',
})
.min(1, 'Mohon pilih setidaknya item'),
learningAbility: z.string({
required_error: 'Mohon pilih kemampuan belajar',
}),
learningWillingness: z.string({
required_error: 'Mohon pilih kemauan belajar',
}),
barrier: z.string({
required_error: 'Mohon pilih hambatan',
}),
learningMethod: z.string({
required_error: 'Mohon pilih metode pembelajaran',
}),
language: z.string({
required_error: 'Mohon pilih bahasa',
}),
languageBarrier: z.string({
required_error: 'Mohon pilih hambatan bahasa',
}),
beliefValue: z.string({
required_error: 'Mohon pilih keyakinan pada nilai-nilai yang dianut',
}),
})
const form = ref<ExposedForm<any> | null>(null)
async function handleActionClick(eventType: string) {
if (eventType === 'submit') {
const isValid = await form.value?.validate()
console.log(isValid)
}
if (eventType === 'cancel') {
}
}
</script>
<template>
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Tambah Asesmen Edukasi</div>
<AppAssessmentEducationEntryForm
ref="form"
:schema="schema"
/>
<div class="my-2 flex justify-end py-2">
<Action @click="handleActionClick" />
</div>
</template>