Merge pull request #222 from dikstub-rssa/feat/education-assessment-79
Feat/education assessment 79
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import { FieldArray, useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
||||
// types
|
||||
// import { type AssessmentEducationFormData, AssessmentEducationSchema, encode } from '~/schemas/assessment-education'
|
||||
|
||||
// componenets
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import Separator from '~/components/pub/ui/separator/Separator.vue'
|
||||
import { BaseSelect, CheckboxGeneral, CheckboxSpecial } from './fields'
|
||||
import { ButtonAction, TextAreaInput } from '~/components/pub/my-ui/form'
|
||||
import ActionForm from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
|
||||
// constant
|
||||
import {
|
||||
abilityCode,
|
||||
willCode,
|
||||
medObstacleCode,
|
||||
learnMethodCode,
|
||||
langClassCode,
|
||||
translatorSrcCode,
|
||||
} from '~/lib/clinical.constants'
|
||||
|
||||
interface FormData {}
|
||||
interface Props {
|
||||
noteLimit?: number
|
||||
isLoading?: boolean
|
||||
isReadonly?: boolean
|
||||
mode: 'add' | 'edit' | 'view'
|
||||
initialValues?: Partial<FormData>
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isLoading: false,
|
||||
isReadonly: false,
|
||||
noteLimit: 10,
|
||||
})
|
||||
const isDisabled = computed(() => props.isLoading || props.isReadonly)
|
||||
// const formSchema = toTypedSchema(AssessmentEducationSchema)
|
||||
const formSchema = toTypedSchema(z.object({}))
|
||||
const { errors, handleSubmit, values, meta, resetForm, setFieldValue, setValues, validate } = useForm<FormData>({
|
||||
name: 'assessmentEducationForm',
|
||||
validationSchema: formSchema,
|
||||
initialValues: props.initialValues
|
||||
? props.initialValues
|
||||
: {
|
||||
plans: [
|
||||
{
|
||||
id: 1,
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
validateOnMount: false,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
resetForm,
|
||||
setValues,
|
||||
values,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="">
|
||||
<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"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
<CheckboxSpecial
|
||||
field-name="specificEducationNeeds"
|
||||
label="Edukasi Khusus"
|
||||
:col-span="2"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
</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"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="learningWillingness"
|
||||
label="Kemauan Belajar"
|
||||
:items="willCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="barrier"
|
||||
label="Hambatan"
|
||||
:items="medObstacleCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="learningMethod"
|
||||
label="Metode Pembelajaran"
|
||||
:items="learnMethodCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="language"
|
||||
label="Bahasa"
|
||||
:items="langClassCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="languageBarrier"
|
||||
label="Hambatan Bahasa"
|
||||
:items="translatorSrcCode"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
|
||||
<BaseSelect
|
||||
field-name="beliefValue"
|
||||
label="Keyakinan pada Nilai-Nilai yang Dianut"
|
||||
:items="{
|
||||
ya: 'IYA',
|
||||
tidak: 'TIDAK',
|
||||
}"
|
||||
:is-disabled="isDisabled"
|
||||
/>
|
||||
</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
|
||||
:col-count="1"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<FieldArray
|
||||
v-slot="{ fields, push, remove }"
|
||||
name="tissueNotes"
|
||||
>
|
||||
<template v-if="fields.length === 0">
|
||||
{{ push({ note: '' }) }}
|
||||
</template>
|
||||
<div class="space-y-4">
|
||||
<DE.Block
|
||||
v-for="(field, idx) in fields"
|
||||
:key="field.key"
|
||||
:col-count="6"
|
||||
:cell-flex="false"
|
||||
>
|
||||
<DE.Cell :col-span="5">
|
||||
<TextAreaInput
|
||||
:field-name="`plans[${idx}].value`"
|
||||
:label="'Rencana ' + Number(idx + 1)"
|
||||
placeholder="Masukkan rencana catatan"
|
||||
:col-span="2"
|
||||
:rows="5"
|
||||
:is-disabled="isReadonly"
|
||||
/>
|
||||
</DE.Cell>
|
||||
<DE.Cell class="flex items-start justify-start">
|
||||
<DE.Field :class="idx === 0 ? 'mt-[30px]' : 'mt-0'">
|
||||
<ButtonAction
|
||||
v-if="idx !== 0"
|
||||
:disabled="isReadonly"
|
||||
preset="delete"
|
||||
:title="`Hapus Rencana ${idx + 1}`"
|
||||
icon-only
|
||||
@click="remove(idx)"
|
||||
/>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</div>
|
||||
|
||||
<div class="self-center pt-3">
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
label="Tambah Rencana"
|
||||
title="Tambah Rencana Edukasi"
|
||||
:disabled="fields.length >= noteLimit || isReadonly"
|
||||
:full-width-mobile="true"
|
||||
class="mt-4"
|
||||
@click="push({ id: fields.length + 1, value: '' })"
|
||||
/>
|
||||
</div>
|
||||
</FieldArray>
|
||||
</DE.Block>
|
||||
<!-- todo -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ActionForm @click="() => {}" />
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
@@ -0,0 +1,66 @@
|
||||
<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[]
|
||||
isDisabled?: boolean
|
||||
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
|
||||
:disabled="isDisabled"
|
||||
: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,79 @@
|
||||
<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 opts = computed(() => {
|
||||
const data = mapToComboboxOptList(props.items)
|
||||
// hide code on select options
|
||||
const filtered = data
|
||||
.filter((item) => item.code)
|
||||
.reduce(
|
||||
(acc, item) => {
|
||||
acc.push({
|
||||
label: item.label as string,
|
||||
value: item.value as string,
|
||||
})
|
||||
return acc
|
||||
},
|
||||
[] as Array<{ label: string; value: string }>,
|
||||
)
|
||||
|
||||
return filtered
|
||||
})
|
||||
</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="opts"
|
||||
: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,25 @@
|
||||
<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
|
||||
isDisabled?: boolean
|
||||
colSpan?: number
|
||||
}
|
||||
defineProps<Props>()
|
||||
|
||||
const generalItems = mapToCheckItems(generalEduCode)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCheckbox
|
||||
:field-name="fieldName"
|
||||
:label="label"
|
||||
:is-disabled="isDisabled"
|
||||
: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,4 @@
|
||||
export { default as BaseSelect } from './base-select.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,75 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,75 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import { id } from 'date-fns/locale'
|
||||
|
||||
import type { Config, RecComponent } from '~/components/pub/my-ui/data-table'
|
||||
import type { ActionReportData } from '~/components/app/action-report/sample'
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{ width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 120 }, { width: 50 }],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TANGGAL' },
|
||||
{ label: 'INFORMASI UMUM' },
|
||||
{ label: 'EDUKASI KHUSUS' },
|
||||
{ label: 'RENCANA EDUKASI' },
|
||||
{ label: 'PELAKSANAAN' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['reportAt', 'dpjp', 'operator', 'operationAt', 'operationType', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'dokter', label: 'Dokter' },
|
||||
{ key: 'reportAt', label: 'Tanggal Laporan' },
|
||||
],
|
||||
|
||||
parses: {
|
||||
reportAt: (rec: unknown): unknown => {
|
||||
const attr = (rec as ActionReportData).reportAt
|
||||
const result = format(new Date(attr), 'd MMMM yyyy, HH:mm', { locale: id })
|
||||
|
||||
return result
|
||||
},
|
||||
operationAt: (rec: unknown): unknown => {
|
||||
return '1 Rencana Edukasi'
|
||||
},
|
||||
system: (rec: unknown): unknown => {
|
||||
return 'Cito'
|
||||
},
|
||||
operator: (rec: unknown): unknown => {
|
||||
return '2 Edukasi dipilih'
|
||||
},
|
||||
billing: (rec: unknown): unknown => {
|
||||
return 'General'
|
||||
},
|
||||
operationType: (rec: unknown): unknown => {
|
||||
return '-'
|
||||
},
|
||||
dpjp: (rec: unknown): unknown => {
|
||||
return '3 Informasi Dipilih'
|
||||
},
|
||||
parent: (rec: unknown): unknown => {
|
||||
const recX = rec as any
|
||||
return recX.parent?.name || '-'
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
const res: RecComponent = {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
return res
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Types
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
|
||||
// Configs
|
||||
import { config } from './list.cfg'
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,54 @@
|
||||
import { addWeeks, formatISO } from 'date-fns'
|
||||
|
||||
export type ActionReportData = {
|
||||
id: number
|
||||
reportAt: string
|
||||
operationAt: string
|
||||
noRm: string
|
||||
noBill: string
|
||||
nama: string
|
||||
jk: string
|
||||
alamat: string
|
||||
klinik: string
|
||||
dokter: string
|
||||
caraBayar: string
|
||||
rujukan: string
|
||||
ketRujukan: string
|
||||
asal: string
|
||||
}
|
||||
|
||||
export const sampleRows: ActionReportData[] = [
|
||||
{
|
||||
id: 1,
|
||||
reportAt: formatISO(addWeeks(new Date(), -1)),
|
||||
operationAt: formatISO(addWeeks(new Date(), 1)),
|
||||
noRm: 'RM23311224',
|
||||
noBill: '-',
|
||||
nama: 'Ahmad Baidowi',
|
||||
jk: 'L',
|
||||
alamat: 'Jl Jaksa Agung S. No. 9',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reportAt: new Date().toISOString(),
|
||||
operationAt: formatISO(addWeeks(new Date(), 2)),
|
||||
noRm: 'RM23455667',
|
||||
noBill: '-',
|
||||
nama: 'Abraham Sulaiman',
|
||||
jk: 'L',
|
||||
alamat: 'Purwantoro, Blimbing',
|
||||
klinik: 'Penyakit dalam',
|
||||
dokter: 'Dr. Andreas Sutaji',
|
||||
caraBayar: 'JKN',
|
||||
rujukan: 'Faskes BPJS',
|
||||
ketRujukan: 'RUMAH SAKIT - RS Lawang Medika - Malang',
|
||||
asal: 'Rawat Jalan Reguler',
|
||||
},
|
||||
// tambahkan lebih banyak baris contoh jika perlu
|
||||
]
|
||||
@@ -39,7 +39,7 @@ defineExpose({
|
||||
<div>
|
||||
<p
|
||||
v-if="props.title"
|
||||
class="text-sm 2xl:text-base mb-2 2xl:mb-3 font-semibold"
|
||||
class="mb-2 text-sm font-semibold 2xl:mb-3 2xl:text-base"
|
||||
>
|
||||
{{ props.title || 'Kontak Pasien' }}
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import List from './list.vue'
|
||||
import Form from './form.vue'
|
||||
import View from './view.vue'
|
||||
|
||||
// Models
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
encounter: Encounter
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { mode, goToEntry, goToView } = useQueryCRUDMode('mode')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<List
|
||||
v-if="mode === 'list'"
|
||||
:encounter="props.encounter"
|
||||
@add="goToEntry"
|
||||
@edit="goToEntry({ fromView: false })"
|
||||
@view="goToView"
|
||||
/>
|
||||
<View
|
||||
v-else-if="mode === 'view'"
|
||||
:encounter="props.encounter"
|
||||
/>
|
||||
<Form v-else />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,74 @@
|
||||
<script setup lang="ts">
|
||||
import mockData from './sample'
|
||||
|
||||
// type
|
||||
import { genDoctor, type Doctor } from '~/models/doctor'
|
||||
import type { ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
|
||||
// components
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import AppAssessmentEducationEntry from '~/components/app/assessment-education/entry.vue'
|
||||
|
||||
// states
|
||||
const route = useRoute()
|
||||
const { mode, goBack } = useQueryCRUDMode('mode')
|
||||
const { recordId } = useQueryCRUDRecordId('record-id')
|
||||
const reportData = ref<ActionReportFormData>({} as unknown as ActionReportFormData)
|
||||
const doctors = ref<Doctor[]>([])
|
||||
const isLoading = ref<boolean>(false)
|
||||
|
||||
// TODO: dummy data
|
||||
;(() => {
|
||||
doctors.value = [genDoctor()]
|
||||
})()
|
||||
|
||||
const entryMode = ref<'add' | 'edit' | 'view'>('add')
|
||||
const isDataReady = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
if (mode.value === 'entry' && recordId.value) {
|
||||
entryMode.value = 'edit'
|
||||
await loadEntryForEdit(+recordId.value)
|
||||
} else {
|
||||
// Untuk mode 'add', langsung set ready
|
||||
isDataReady.value = true
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: map data
|
||||
async function loadEntryForEdit(id: number | string) {
|
||||
isLoading.value = true
|
||||
const result = mockData
|
||||
reportData.value = result as ActionReportFormData
|
||||
isLoading.value = false
|
||||
isDataReady.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppAssessmentEducationEntry
|
||||
v-if="isDataReady"
|
||||
:isLoading="isLoading"
|
||||
:mode="entryMode"
|
||||
@submit="(val: {}) => console.log(val)"
|
||||
@back="goBack"
|
||||
@error="
|
||||
(err: Error) => {
|
||||
toast({
|
||||
title: 'Terjadi Kesalahan',
|
||||
description: err.message,
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
"
|
||||
:doctors="doctors"
|
||||
:initialValues="reportData"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center justify-center p-8"
|
||||
>
|
||||
<p class="text-muted-foreground">Memuat data...</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,276 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import AppAssessmentEducationList from '~/components/app/assessment-education/list.vue'
|
||||
import AppAssessmentEducationListHistory from '~/components/app/assessment-education/list-history.vue'
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { ButtonAction } from '~/components/pub/my-ui/form'
|
||||
|
||||
// config
|
||||
import { config } from '~/components/app/assessment-education/list.cfg'
|
||||
|
||||
// types
|
||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
|
||||
// Samples
|
||||
import { sampleRows, type AssessmentEducationData } from '~/components/app/assessment-education/sample'
|
||||
import sampleReport from './sample'
|
||||
|
||||
// helpers
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
encounter: Encounter
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emits = defineEmits<{
|
||||
(e: 'add'): void
|
||||
(e: 'edit', id: number | string): void
|
||||
(e: 'view', id: number | string): void
|
||||
}>()
|
||||
|
||||
// states
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { goToEntry, backToList } = useQueryCRUDMode('mode')
|
||||
const title = ref('')
|
||||
const search = ref('')
|
||||
const dateFrom = ref('')
|
||||
const dateTo = ref('')
|
||||
const isDialogOpen = ref<boolean>(false)
|
||||
const isLoading = ref<boolean>(false)
|
||||
|
||||
// #region mock
|
||||
// Handlers
|
||||
import {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} from '~/handlers/consultation.handler'
|
||||
// #endregion
|
||||
|
||||
// filter + pencarian sederhana (client-side)
|
||||
const filtered = computed(() => {
|
||||
const q = search.value.trim().toLowerCase()
|
||||
return sampleRows.filter((r: ActionReportData) => {
|
||||
if (q) {
|
||||
return r.nama.toLowerCase().includes(q) || r.noRm.toLowerCase().includes(q) || r.dokter.toLowerCase().includes(q)
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
const goEdit = (id: number | string) => {
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
mode: 'entry',
|
||||
'record-id': id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const goView = (id: number | string) => {
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: {
|
||||
...route.query,
|
||||
mode: 'view',
|
||||
'record-id': id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
provide('rec_id', recId)
|
||||
provide('rec_action', recAction)
|
||||
provide('rec_item', recItem)
|
||||
provide('table_data_loader', isLoading)
|
||||
|
||||
async function onGetDetail(id: number | string) {
|
||||
isLoading.value = true
|
||||
const res = sampleReport
|
||||
recItem.value = res
|
||||
console.log(res)
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// #region watcher
|
||||
watch([recId, recAction], (newVal) => {
|
||||
const [id, action] = newVal
|
||||
|
||||
// Guard: jangan proses jika id = 0 atau action kosong
|
||||
if (!id || !action) return
|
||||
|
||||
switch (action) {
|
||||
case ActionEvents.showDetail:
|
||||
// onGetDetail(recId.value)
|
||||
goView(id)
|
||||
title.value = 'Detail Konsultasi'
|
||||
break
|
||||
case ActionEvents.showEdit:
|
||||
goEdit(id)
|
||||
title.value = 'Edit Konsultasi'
|
||||
break
|
||||
case ActionEvents.showConfirmDelete:
|
||||
isRecordConfirmationOpen.value = true
|
||||
break
|
||||
}
|
||||
|
||||
// Reset KEDUANYA menggunakan nextTick agar tidak trigger watcher lagi
|
||||
nextTick(() => {
|
||||
recId.value = 0
|
||||
recAction.value = ''
|
||||
})
|
||||
})
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mx-auto max-w-full">
|
||||
<div class="border-b p-6">
|
||||
<h1 class="text-2xl font-semibold">Asesmen Kebutuhan Edukasi</h1>
|
||||
<p class="mt-1 text-sm text-gray-500">Manajemen asesmen kebutuhan edukasi pasien rawat jalan</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3 border-b p-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
v-model="search"
|
||||
placeholder="Cari Nama / No.RM"
|
||||
class="w-64 rounded border px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
v-model="dateFrom"
|
||||
type="date"
|
||||
class="rounded border px-3 py-2"
|
||||
/>
|
||||
<span class="text-sm text-gray-500">-</span>
|
||||
<input
|
||||
v-model="dateTo"
|
||||
type="date"
|
||||
class="rounded border px-3 py-2"
|
||||
/>
|
||||
<ButtonAction
|
||||
preset="custom"
|
||||
title="Filter List Asesmen"
|
||||
label="Filter"
|
||||
icon="i-lucide-filter"
|
||||
@click="
|
||||
() => {
|
||||
isDialogOpen = true
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<ButtonAction
|
||||
preset="custom"
|
||||
title="Riwayat"
|
||||
icon="i-lucide-history"
|
||||
label="Riwayat"
|
||||
@click="
|
||||
() => {
|
||||
isDialogOpen = true
|
||||
}
|
||||
"
|
||||
/>
|
||||
<ButtonAction
|
||||
preset="add"
|
||||
title="Tambah Asesmen"
|
||||
icon="i-lucide-plus"
|
||||
label="Asesmen"
|
||||
@click="
|
||||
() => {
|
||||
goToEntry()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto p-4">
|
||||
<AppAssessmentEducationList
|
||||
:data="filtered"
|
||||
:pagination-meta="{
|
||||
recordCount: 2,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 1,
|
||||
hasPrev: false,
|
||||
hasNext: false,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
v-model:open="isDialogOpen"
|
||||
title="Arsip Riwayat Asesmen Kebutuhan Edukasi"
|
||||
size="2xl"
|
||||
prevent-outside
|
||||
@update:open="
|
||||
(value: any) => {
|
||||
isDialogOpen = value
|
||||
}
|
||||
"
|
||||
>
|
||||
<AppAssessmentEducationListHistory
|
||||
:data="filtered"
|
||||
:pagination-meta="{
|
||||
recordCount: 2,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
totalPage: 1,
|
||||
hasPrev: false,
|
||||
hasNext: false,
|
||||
}"
|
||||
/>
|
||||
</Dialog>
|
||||
|
||||
<RecordConfirmation
|
||||
v-model:open="isRecordConfirmationOpen"
|
||||
action="delete"
|
||||
:record="recItem"
|
||||
@confirm="
|
||||
() =>
|
||||
handleActionRemove(
|
||||
recItem.id,
|
||||
() => {
|
||||
router.go(0)
|
||||
},
|
||||
toast,
|
||||
)
|
||||
"
|
||||
@cancel=""
|
||||
>
|
||||
<template #default="{ record }">
|
||||
{{ console.log(JSON.stringify(record)) }}
|
||||
<div class="space-y-1 text-sm">
|
||||
<p
|
||||
v-for="field in config.delKeyNames"
|
||||
:key="field.key"
|
||||
:v-if="record?.[field.key]"
|
||||
>
|
||||
<span class="font-semibold">{{ field.label }}:</span>
|
||||
{{ record[field.key] }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
export default {
|
||||
operatorTeam: {
|
||||
dpjpId: -1,
|
||||
operatorName: 'Julian Alvarez',
|
||||
assistantOperatorName: 'Arda Guller',
|
||||
instrumentNurseName: 'Kenan Yildiz',
|
||||
surgeryDate: '2025-11-13T14:29:00',
|
||||
actionDiagnosis: 'Sprei gratisnya mana',
|
||||
},
|
||||
procedures: [
|
||||
{
|
||||
id: -1,
|
||||
name: 'Ndase mumet',
|
||||
code: 'CX1',
|
||||
},
|
||||
],
|
||||
operationExecution: {
|
||||
surgeryType: 'khusus',
|
||||
billingCode: 'local',
|
||||
operationSystem: 'cito',
|
||||
surgeryCleanType: 'kotor',
|
||||
surgeryNumber: 'retry',
|
||||
birthPlaceNote: 'out3',
|
||||
personWeight: 100,
|
||||
operationDescription: 'asdsadsa1',
|
||||
birthRemark: 'lahir_hidup',
|
||||
|
||||
operationStartAt: '2025-11-13T14:29:00',
|
||||
operationEndAt: '2025-11-13T17:29:00',
|
||||
|
||||
anesthesiaStartAt: '2025-11-13T11:29:00',
|
||||
anesthesiaEndAt: '2025-11-13T18:29:00',
|
||||
},
|
||||
bloodInput: {
|
||||
type: 'tc',
|
||||
amount: {
|
||||
prc: null,
|
||||
wb: null,
|
||||
ffp: null,
|
||||
tc: 3243324,
|
||||
},
|
||||
},
|
||||
implant: {
|
||||
brand: 'Samsung',
|
||||
name: 'S.Komedi',
|
||||
companionName: 'When ya',
|
||||
},
|
||||
specimen: {
|
||||
destination: 'pa',
|
||||
},
|
||||
tissueNotes: [
|
||||
{
|
||||
note: 'Anjai',
|
||||
},
|
||||
{
|
||||
note: 'Ciee Kaget',
|
||||
},
|
||||
{
|
||||
note: 'Baper',
|
||||
},
|
||||
{
|
||||
note: 'Saltink weeh',
|
||||
},
|
||||
{
|
||||
note: 'Kaburrr',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import mockData from './sample'
|
||||
|
||||
// types
|
||||
import { type ActionReportFormData } from '~/schemas/action-report.schema'
|
||||
import { type Encounter } from '~/models/encounter'
|
||||
|
||||
// Components
|
||||
import AppActionReportPreview from '~/components/app/action-report/preview.vue'
|
||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
// #region Props & Emits
|
||||
const router = useRouter()
|
||||
const { backToList, goToEntry } = useQueryCRUDMode('mode')
|
||||
const { recordId } = useQueryCRUDRecordId('record-id')
|
||||
|
||||
function onEditFromView() {
|
||||
goToEntry({ fromView: true })
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
encounter: Encounter
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const reportData = ref<ActionReportFormData | null>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: 'Detail Laporan Tindakan',
|
||||
icon: 'i-lucide-stethoscope',
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(async () => {
|
||||
reportData.value = mockData as unknown as ActionReportFormData
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
function onEdit() {
|
||||
router.push({
|
||||
name: 'action-report-id-edit',
|
||||
params: { id: 100 },
|
||||
})
|
||||
}
|
||||
function onBack() {}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppActionReportPreview
|
||||
v-if="reportData"
|
||||
:data="reportData"
|
||||
@back="backToList"
|
||||
@edit="onEditFromView"
|
||||
/>
|
||||
</template>
|
||||
@@ -1,33 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
height?: number
|
||||
class?: string
|
||||
activeTab?: 1 | 2
|
||||
}>()
|
||||
|
||||
const classVal = computed(() => {
|
||||
return props.class ? props.class : ''
|
||||
})
|
||||
const activeTab = ref(props.activeTab || 1)
|
||||
|
||||
function switchActiveTab() {
|
||||
activeTab.value = activeTab.value === 1 ? 2 : 1
|
||||
function handleClick(value: 1 | 2) {
|
||||
activeTab.value = value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="`content-switcher ${classVal}`" :style="height ? `height:${200}px` : ''">
|
||||
<div class="wrapper">
|
||||
<div :class="`item item-1 ${activeTab === 1 ? 'active' : 'inactive'}`">
|
||||
<slot name="content1" />
|
||||
<div class="content-switcher" :style="`height: ${height || 200}px`">
|
||||
<div :class="`${activeTab === 1 ? 'active' : 'inactive'}`">
|
||||
<div class="content-wrapper">
|
||||
<div>
|
||||
<slot name="content1" />
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`nav border-slate-300 ${ activeTab == 1 ? 'border-l' : 'border-r'}`">
|
||||
<button @click="switchActiveTab()" class="!p-0 w-full h-full">
|
||||
<Icon :name="activeTab == 1 ? 'i-lucide-chevron-left' : 'i-lucide-chevron-right'" class="text-3xl" />
|
||||
<div class="content-nav">
|
||||
<button @click="handleClick(1)">
|
||||
<Icon name="i-lucide-chevron-right" />
|
||||
</button>
|
||||
</div>
|
||||
<div :class="`item item-2 ${activeTab === 2 ? 'active' : 'inactive'}`">
|
||||
<slot name="content2" />
|
||||
</div>
|
||||
<div :class="`${activeTab === 2 ? 'active' : 'inactive'}`">
|
||||
<div class="content-nav">
|
||||
<button @click="handleClick(2)">
|
||||
<Icon name="i-lucide-chevron-left" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
<div>
|
||||
<slot name="content2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,24 +42,45 @@ function switchActiveTab() {
|
||||
|
||||
<style>
|
||||
.content-switcher {
|
||||
@apply overflow-hidden
|
||||
@apply flex overflow-hidden gap-3
|
||||
}
|
||||
.wrapper {
|
||||
@apply flex w-[200%] h-full
|
||||
}
|
||||
.item {
|
||||
@apply w-[calc(50%-60px)]
|
||||
.content-switcher > * {
|
||||
@apply border border-slate-300 rounded-md flex overflow-hidden
|
||||
}
|
||||
|
||||
.item-1.active {
|
||||
@apply ms-0 transition-all duration-500 ease-in-out
|
||||
.content-wrapper {
|
||||
@apply p-4 2xl:p-5 overflow-hidden grow
|
||||
}
|
||||
.item-1.inactive {
|
||||
@apply -ms-[calc(50%-60px)] transition-all duration-500 ease-in-out
|
||||
.inactive .content-wrapper {
|
||||
@apply p-0 w-0
|
||||
}
|
||||
|
||||
.nav {
|
||||
@apply h-full w-[60px] flex flex-row items-center justify-center content-center !text-2xl overflow-hidden
|
||||
.content-nav {
|
||||
@apply h-full flex flex-row items-center justify-center content-center !text-2xl overflow-hidden
|
||||
}
|
||||
|
||||
.content-nav button {
|
||||
@apply pt-2 px-2 h-full w-full
|
||||
}
|
||||
|
||||
/* .content-switcher .inactive > .content-wrapper {
|
||||
@apply w-0 p-0 opacity-0 transition-all duration-500 ease-in-out
|
||||
} */
|
||||
.content-switcher .inactive {
|
||||
@apply w-16 transition-all duration-500 ease-in-out
|
||||
}
|
||||
.content-switcher .inactive > .content-nav {
|
||||
@apply w-full transition-all duration-100 ease-in-out
|
||||
}
|
||||
|
||||
.content-switcher .active {
|
||||
@apply grow transition-all duration-500 ease-in-out
|
||||
}
|
||||
.content-switcher .active > .content-nav {
|
||||
@apply w-0 transition-all duration-100 ease-in-out
|
||||
}
|
||||
/* .content-switcher .active > .content-wrapper {
|
||||
@apply w-full delay-1000 transition-all duration-1000 ease-in-out
|
||||
} */
|
||||
|
||||
</style>
|
||||
|
||||
@@ -53,8 +53,8 @@ const settingClass = computed(() => {
|
||||
'[&_.cell]:2xl:flex',
|
||||
][getBreakpointIdx(props.cellFlexPoint)]
|
||||
cls += ' [&_.label]:flex-shrink-0 ' + [
|
||||
'[&_.label]:md:w-16 [&_.label]:xl:w-20',
|
||||
'[&_.label]:md:w-20 [&_.label]:xl:w-24',
|
||||
'[&_.label]:md:w-12 [&_.label]:xl:w-20',
|
||||
'[&_.label]:md:w-16 [&_.label]:xl:w-24',
|
||||
'[&_.label]:md:w-24 [&_.label]:xl:w-32',
|
||||
'[&_.label]:md:w-32 [&_.label]:xl:w-40',
|
||||
'[&_.label]:md:w-44 [&_.label]:xl:w-52',
|
||||
|
||||
@@ -4,10 +4,6 @@ import { type EncounterItem } from "~/handlers/encounter-init.handler";
|
||||
const props = defineProps<{
|
||||
initialActiveMenu: string
|
||||
data: EncounterItem[]
|
||||
canCreate?: boolean
|
||||
canRead?: boolean
|
||||
canUpdate?: boolean
|
||||
canDelete?: boolean
|
||||
}>()
|
||||
|
||||
const activeMenu = ref(props.initialActiveMenu)
|
||||
@@ -42,12 +38,7 @@ function changeMenu(value: string) {
|
||||
class="flex-1 rounded-md border bg-white p-4 shadow-sm dark:bg-neutral-950">
|
||||
<component
|
||||
:is="data.find((m) => m.id === activeMenu)?.component"
|
||||
v-bind="data.find((m) => m.id === activeMenu)?.props"
|
||||
:can-create="canCreate"
|
||||
:can-read="canRead"
|
||||
:can-update="canUpdate"
|
||||
:can-delete="canDelete"
|
||||
/>
|
||||
v-bind="data.find((m) => m.id === activeMenu)?.props" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,17 +30,15 @@ export function useQueryCRUD(modeKey: string = 'mode', recordIdKey: string = 're
|
||||
})
|
||||
|
||||
const goToEntry = (myRecord_id?: any) => {
|
||||
if(myRecord_id) {
|
||||
crudQueryParams.value = { mode: 'entry', recordId: myRecord_id }
|
||||
if (myRecord_id) {
|
||||
crudQueryParams.value.mode = 'entry'
|
||||
crudQueryParams.value.recordId = myRecord_id
|
||||
} else {
|
||||
crudQueryParams.value = { mode: 'entry', recordId: undefined }
|
||||
crudQueryParams.value.mode = 'entry'
|
||||
crudQueryParams.value.recordId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
const goToDetail = (myRecord_id: string|number) => {
|
||||
crudQueryParams.value = { mode: 'list', recordId: String(myRecord_id) }
|
||||
}
|
||||
|
||||
const backToList = () => {
|
||||
delete route.query[recordIdKey]
|
||||
router.push({
|
||||
@@ -52,7 +50,7 @@ export function useQueryCRUD(modeKey: string = 'mode', recordIdKey: string = 're
|
||||
})
|
||||
}
|
||||
|
||||
return { crudQueryParams, goToEntry, goToDetail, backToList }
|
||||
return { crudQueryParams, goToEntry, backToList }
|
||||
}
|
||||
|
||||
export function useQueryCRUDMode(key: string = 'mode') {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Handlers
|
||||
import { genCrudHandler } from '~/handlers/_handler'
|
||||
|
||||
// Services
|
||||
import { create, update, remove } from '~/services/assessment-education.service'
|
||||
|
||||
export const {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} = genCrudHandler({
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
})
|
||||
@@ -46,15 +46,18 @@ const ApLabOrderAsync = defineAsyncComponent(() => import('~/components/content/
|
||||
const CprjAsync = defineAsyncComponent(() => import('~/components/content/cprj/entry.vue'))
|
||||
const RadiologyAsync = defineAsyncComponent(() => import('~/components/content/radiology-order/main.vue'))
|
||||
const ConsultationAsync = defineAsyncComponent(() => import('~/components/content/consultation/list.vue'))
|
||||
const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/main.vue'))
|
||||
const DocUploadListAsync = defineAsyncComponent(() => import('~/components/content/document-upload/list.vue'))
|
||||
const GeneralConsentListAsync = defineAsyncComponent(() => import('~/components/content/general-consent/entry.vue'))
|
||||
const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/main.vue'))
|
||||
const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/main.vue'))
|
||||
const KfrListAsync = defineAsyncComponent(() => import('~/components/content/kfr/main.vue'))
|
||||
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/main.vue'))
|
||||
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/main.vue'))
|
||||
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/main.vue'))
|
||||
const ResumeListAsync = defineAsyncComponent(() => import('~/components/content/resume/list.vue'))
|
||||
const ControlLetterListAsync = defineAsyncComponent(() => import('~/components/content/control-letter/list.vue'))
|
||||
const KfrListAsync = defineAsyncComponent(() => import('~/components/content/kfr/list.vue'))
|
||||
const PrbListAsync = defineAsyncComponent(() => import('~/components/content/prb/list.vue'))
|
||||
const SurgeryReportListAsync = defineAsyncComponent(() => import('~/components/content/surgery-report/list.vue'))
|
||||
const VaccineDataListAsync = defineAsyncComponent(() => import('~/components/content/vaccine-data/list.vue'))
|
||||
const InitialNursingStudyAsync = defineAsyncComponent(() => import('~/components/content/initial-nursing/entry.vue'))
|
||||
const AssessmentEducationEntryAsync = defineAsyncComponent(
|
||||
() => import('~/components/content/assessment-education/entry.vue'),
|
||||
)
|
||||
const SummaryMedicAsync = defineAsyncComponent(() => import('~/components/content/summary-medic/entry.vue'))
|
||||
const ActionReportEntryAsync = defineAsyncComponent(() => import('~/components/content/action-report/entry.vue'))
|
||||
|
||||
@@ -65,19 +68,17 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
// NOTE : HIDDEN UNTIL IT IS READY
|
||||
// earlyNurseryAssessment: {
|
||||
// id: 'early-nursery-assessment',
|
||||
// title: 'Pengkajian Awal Keperawatan',
|
||||
// classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
// unit: 'all',
|
||||
// },
|
||||
earlyNurseryAssessment: {
|
||||
id: 'early-nursery-assessment',
|
||||
title: 'Pengkajian Awal Keperawatan',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
earlyMedicalAssessment: {
|
||||
id: 'early-medical-assessment',
|
||||
title: 'Pengkajian Awal Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
earlyMedicalRehabAssessment: {
|
||||
id: 'rehab-medical-assessment',
|
||||
@@ -86,27 +87,20 @@ const defaultKeys: Record<string, any> = {
|
||||
unit: 'rehab',
|
||||
afterId: 'early-medical-assessment',
|
||||
},
|
||||
fkr: {
|
||||
id: 'fkr',
|
||||
title: 'FKR',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
functionAssessment: {
|
||||
id: 'function-assessment',
|
||||
title: 'Asesmen Fungsi',
|
||||
classCode: ['ambulatory'],
|
||||
unit: 'rehab',
|
||||
afterId: 'rehab-medical-assessment',
|
||||
},
|
||||
therapyProtocol: {
|
||||
id: 'therapy-protocol',
|
||||
classCode: ['ambulatory'],
|
||||
title: 'Protokol Terapi',
|
||||
unit: 'rehab',
|
||||
afterId: 'function-assessment',
|
||||
},
|
||||
// NOTE: Replaced by FRK
|
||||
// functionAssessment: {
|
||||
// id: 'function-assessment',
|
||||
// title: 'Asesmen Fungsi',
|
||||
// classCode: ['ambulatory'],
|
||||
// unit: 'rehab',
|
||||
// afterId: 'rehab-medical-assessment',
|
||||
// },
|
||||
// therapyProtocol: {
|
||||
// id: 'therapy-protocol',
|
||||
// classCode: ['ambulatory'],
|
||||
// title: 'Protokol Terapi',
|
||||
// unit: 'rehab',
|
||||
// afterId: 'function-assessment',
|
||||
// },
|
||||
chemotherapyProtocol: {
|
||||
id: 'chemotherapy-protocol',
|
||||
title: 'Protokol Kemoterapi',
|
||||
@@ -229,6 +223,12 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency'],
|
||||
unit: 'all',
|
||||
},
|
||||
kfr: {
|
||||
id: 'kfr',
|
||||
title: 'KFR',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
refBack: {
|
||||
id: 'reference-back',
|
||||
title: 'PRB',
|
||||
@@ -265,12 +265,6 @@ const defaultKeys: Record<string, any> = {
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
summaryMedic: {
|
||||
id: 'summary-medic',
|
||||
title: 'Profil Ringkasan Medis',
|
||||
classCode: ['ambulatory', 'emergency', 'inpatient'],
|
||||
unit: 'all',
|
||||
},
|
||||
initialNursingStudy: {
|
||||
id: 'initial-nursing-study',
|
||||
title: 'Kajian Awal Keperawatan',
|
||||
@@ -362,9 +356,12 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
}
|
||||
}
|
||||
if (currentKeys?.educationAssessment) {
|
||||
// TODO: add component for education assessment
|
||||
currentKeys.educationAssessment['component'] = null
|
||||
currentKeys.educationAssessment['props'] = { encounter_id: id }
|
||||
currentKeys.educationAssessment['component'] = AssessmentEducationEntryAsync
|
||||
currentKeys.educationAssessment['props'] = {
|
||||
encounter: data?.encounter,
|
||||
type: 'education-assessment',
|
||||
label: currentKeys.educationAssessment['title'],
|
||||
}
|
||||
}
|
||||
if (currentKeys?.generalConsent) {
|
||||
currentKeys.generalConsent['component'] = GeneralConsentListAsync
|
||||
@@ -424,9 +421,9 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
currentKeys.refBack['component'] = PrbListAsync
|
||||
currentKeys.refBack['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.fkr) {
|
||||
currentKeys.fkr['component'] = KfrListAsync
|
||||
currentKeys.fkr['props'] = { encounter: data?.encounter }
|
||||
if (currentKeys?.kfr) {
|
||||
currentKeys.kfr['component'] = KfrListAsync
|
||||
currentKeys.kfr['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
if (currentKeys?.screening) {
|
||||
// TODO: add component for screening
|
||||
@@ -450,10 +447,12 @@ export function injectComponents(id: string | number, data: EncounterListData, m
|
||||
currentKeys.priceList['component'] = null
|
||||
currentKeys.priceList['props'] = { encounter_id: id }
|
||||
}
|
||||
if (currentKeys?.summaryMedic) {
|
||||
currentKeys.summaryMedic['component'] = SummaryMedicAsync
|
||||
currentKeys.summaryMedic['props'] = { encounter_id: id }
|
||||
|
||||
if (currentKeys?.initialNursingStudy) {
|
||||
currentKeys.initialNursingStudy['component'] = InitialNursingStudyAsync
|
||||
currentKeys.initialNursingStudy['props'] = { encounter: data?.encounter }
|
||||
}
|
||||
|
||||
if (currentKeys?.initialNursingStudy) {
|
||||
currentKeys.initialNursingStudy['component'] = InitialNursingStudyAsync
|
||||
currentKeys.initialNursingStudy['props'] = { encounter: data?.encounter }
|
||||
@@ -527,12 +526,9 @@ export function mapResponseToEncounter(result: any): any {
|
||||
? result.visitDate
|
||||
: result.registeredAt || result.patient?.registeredAt || null,
|
||||
adm_employee_id: result.adm_employee_id || 0,
|
||||
adm_employee: result.adm_employee || null,
|
||||
responsible_nurse_code: result.responsible_nurse_code || null,
|
||||
responsible_nurse: result.responsible_nurse || null,
|
||||
appointment_doctor_code: result.appointment_doctor_code || null,
|
||||
appointment_doctor_id: result.appointment_doctor_id || null,
|
||||
responsible_doctor_id: result.responsible_doctor_id || null,
|
||||
appointment_doctor: result.appointment_doctor || null,
|
||||
responsible_doctor_code: result.responsible_doctor_id || null,
|
||||
responsible_doctor: result.responsible_doctor || null,
|
||||
refSource_name: result.refSource_name || null,
|
||||
appointment_id: result.appointment_id || null,
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
export const generalEduCode = {
|
||||
'right-obg': 'Hak dan kewajiban pasien dan keluarga',
|
||||
'general-consent': 'General Consent',
|
||||
service: 'Pelayanan yang disediakan (jam pelayanan, akses pelayanan dan proses pelayanan)',
|
||||
'all-care-service': 'Sumber alternatif asuhan di tempat lain/faskes lain',
|
||||
'home-plan': 'Rencana tindakan di rumah',
|
||||
'home-care': 'Kebutuhan perawatan di rumah',
|
||||
orientation: 'Orientasi Ruangan',
|
||||
'fall-risk-prevention': 'Pencegahan risiko jatuh',
|
||||
'alt-care': 'Alternatif pelayanan',
|
||||
'act-delay': 'Penundaan Tindakan',
|
||||
others: 'Lain-lain',
|
||||
} as const
|
||||
|
||||
export type GeneralEduCodeKey = keyof typeof generalEduCode
|
||||
|
||||
export const specialEduCode = {
|
||||
'disease-diag-dev': 'Diagnosa penyakit dan perkembangannya',
|
||||
'safe-med-usage': 'Penggunaan obat yang aman',
|
||||
'side-effect': 'Efek samping dan reaksi obat',
|
||||
diet: 'Diet/Nutrisi',
|
||||
'pain-mgmt': 'Manajemen nyeri',
|
||||
'medical-eq-usage': 'Penggunaan Peralatan Medis',
|
||||
'rehab-technique': 'Teknik Rehabilitasi',
|
||||
'prevention-act': 'Tindakan pencegahan (cuci tangan, pemasangan gelang)',
|
||||
} as const
|
||||
|
||||
export type SpecialEduCodeKey = keyof typeof specialEduCode
|
||||
|
||||
export const eduAssessmentCode = {
|
||||
'learn-ability': 'Kemampuan Belajar',
|
||||
'learn-will': 'Kemauan Belajar',
|
||||
obstacle: 'Hambatan',
|
||||
'learn-method': 'Metode Pembelajaran',
|
||||
lang: 'Bahasa',
|
||||
'lang-obstacle': 'Hambatan Bahasa',
|
||||
belief: 'Keyakinan',
|
||||
} as const
|
||||
|
||||
export type EduAssessmentCodeKey = keyof typeof eduAssessmentCode
|
||||
|
||||
export const abilityCode = {
|
||||
able: 'Mampu',
|
||||
'not-able': 'Tidak Mampu',
|
||||
} as const
|
||||
|
||||
export type AbilityCodeKey = keyof typeof abilityCode
|
||||
|
||||
export const willCode = {
|
||||
ready: 'Siap',
|
||||
interested: 'Tertarik',
|
||||
'not-interested': 'Tidak Tertarik',
|
||||
} as const
|
||||
|
||||
export type WillCodeKey = keyof typeof willCode
|
||||
|
||||
export const medObstacleCode = {
|
||||
hearing: 'Pendengaran',
|
||||
sight: 'Penglihatan',
|
||||
physical: 'Fisik',
|
||||
emotional: 'Emosional',
|
||||
cognitif: 'Kognitif',
|
||||
} as const
|
||||
|
||||
export type MedObstacleCodeKey = keyof typeof medObstacleCode
|
||||
|
||||
export const learnMethodCode = {
|
||||
demo: 'Demonstrasi',
|
||||
'discuss-leaflet': 'Diskusi Leaflet',
|
||||
} as const
|
||||
|
||||
export type LearnMethodCodeKey = keyof typeof learnMethodCode
|
||||
|
||||
export const langClassCode = {
|
||||
ind: 'Indonesia',
|
||||
region: 'Daerah',
|
||||
foreign: 'Asing',
|
||||
} as const
|
||||
|
||||
export type LangClassCodeKey = keyof typeof langClassCode
|
||||
|
||||
export const translatorSrcCode = {
|
||||
team: 'Tim Penerjemah',
|
||||
family: 'Keluarga',
|
||||
} as const
|
||||
|
||||
export type TranslatorSrcCodeKey = keyof typeof translatorSrcCode
|
||||
|
||||
// helpers
|
||||
type EduCodeType = 'general' | 'special'
|
||||
export function serializeKeyToBoolean(type: EduCodeType, selected: string[]): Record<string, boolean> {
|
||||
switch (type) {
|
||||
case 'general': {
|
||||
return Object.keys(generalEduCode).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = selected.includes(key)
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, boolean>,
|
||||
)
|
||||
}
|
||||
|
||||
case 'special': {
|
||||
return Object.keys(specialEduCode).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = selected.includes(key)
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, boolean>,
|
||||
)
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error('unknown type to serialize')
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -9,6 +9,10 @@ export interface SelectOptionType<_T = string> {
|
||||
label: string
|
||||
code?: string
|
||||
}
|
||||
export interface CheckItem {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -29,6 +33,15 @@ export function mapToComboboxOptList(items: Record<string, string>): SelectOptio
|
||||
return result
|
||||
}
|
||||
|
||||
export function mapToCheckItems<T extends Record<string, string>, K extends keyof T & string>(
|
||||
items: T,
|
||||
): { id: K; label: T[K] }[] {
|
||||
return Object.entries(items).map(([key, value]) => ({
|
||||
id: key as K,
|
||||
label: value as T[K],
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengkonversi string menjadi title case (huruf pertama setiap kata kapital)
|
||||
* @param str - String yang akan dikonversi
|
||||
@@ -38,7 +51,6 @@ export function toTitleCase(str: string): string {
|
||||
return str.toLowerCase().replace(/\b\w/g, (char) => char.toUpperCase())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Menghitung umur berdasarkan tanggal lahir
|
||||
* @param birthDate - Tanggal lahir dalam format Date atau string
|
||||
|
||||
@@ -52,7 +52,7 @@ export function genEncounter(): Encounter {
|
||||
patient: genPatient(),
|
||||
registeredAt: '',
|
||||
class_code: '',
|
||||
unit_code: '',
|
||||
unit_code: 0,
|
||||
unit: genUnit(),
|
||||
visitDate: '',
|
||||
adm_employee_id: 0,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import type { Employee } from "./employee"
|
||||
|
||||
export interface Nurse extends Base {
|
||||
employee_id: number
|
||||
employee?: Employee
|
||||
ihs_number?: string
|
||||
unit_id: number
|
||||
infra_id: number
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import type { PagePermission } from '~/models/role'
|
||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||
|
||||
definePageMeta({
|
||||
middleware: ['rbac'],
|
||||
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||
title: 'Tambah Assessment Education',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/patient']
|
||||
|
||||
const { checkRole, hasReadAccess } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
// const hasAccess = checkRole(roleAccess)
|
||||
const hasAccess = true
|
||||
if (!hasAccess) {
|
||||
navigateTo('/403')
|
||||
}
|
||||
|
||||
// Define permission-based computed properties
|
||||
const canRead = hasReadAccess(roleAccess)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentAssessmentEducationAdd />
|
||||
</div>
|
||||
<Error
|
||||
v-else
|
||||
:status-code="403"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -4,9 +4,8 @@ import { InternalReferenceSchema } from './internal-reference.schema'
|
||||
// Check In
|
||||
const CheckInSchema = z.object({
|
||||
// registeredAt: z.string({ required_error: 'Tanggal masuk harus diisi' }),
|
||||
responsible_doctor_code: z.string({ required_error: 'Dokter harus diisi' }),
|
||||
// adm_employee_id: z.number({ required_error: 'PJA harus diisi' }).gt(0, 'PJA harus diisi'),
|
||||
registeredAt: z.string({ required_error: 'waktu harus diisi' }),
|
||||
responsible_doctor_id: z.number({ required_error: 'Dokter harus diisi' }).gt(0, 'Dokter harus diisi'),
|
||||
adm_employee_id: z.number({ required_error: 'PJA harus diisi' }).gt(0, 'PJA harus diisi'),
|
||||
})
|
||||
type CheckInFormData = z.infer<typeof CheckInSchema>
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Base
|
||||
import * as base from './_crud-base'
|
||||
|
||||
// Types
|
||||
import type { EduAssessmentRaw, EduAssessment } from '~/models/edu-assessment'
|
||||
|
||||
const path = '/api/v1/edu-assessment'
|
||||
const name = 'edu-assessment'
|
||||
|
||||
export function create(data: EduAssessmentRaw) {
|
||||
return base.create(path, data, name)
|
||||
}
|
||||
|
||||
export function getList(params: any = null) {
|
||||
return base.getList(path, params, name)
|
||||
}
|
||||
|
||||
export function getDetail(id: number | string) {
|
||||
return base.getDetail(path, id, name)
|
||||
}
|
||||
|
||||
export function update(id: number | string, data: any) {
|
||||
return base.update(path, id, data, name)
|
||||
}
|
||||
|
||||
export function remove(id: number | string) {
|
||||
return base.remove(path, id, name)
|
||||
}
|
||||
Reference in New Issue
Block a user