Merge pull request #181 from dikstub-rssa/feat/kfr-kemoterapi-174
Feat/kfr kemoterapi 174
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '~/lib/utils';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
assesmentDate?: string
|
||||||
|
class?: string
|
||||||
|
}>(), {
|
||||||
|
assesmentDate: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('flex items-center gap-3 p-3 rounded-md text-orange-500 border border-orange-400 bg-orange-50',
|
||||||
|
props.class
|
||||||
|
)">
|
||||||
|
<Icon name="i-lucide-triangle-alert" class="h-9 w-9 align-middle transition-colors" />
|
||||||
|
<p class="font-medium">Pasien telah mencapai atau telah melampaui jadwal Asesment pada
|
||||||
|
<b>{{ new Date(props.assesmentDate).toDateString() }}</b>
|
||||||
|
<br>
|
||||||
|
Harap melakukan Re-Asement sebelum melanjutkan Protocol Therapy</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ActionEvents, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: ListItemDto
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const recId = inject<Ref<number>>('rec_id')!
|
||||||
|
const recAction = inject<Ref<string>>('rec_action')!
|
||||||
|
const recItem = inject<Ref<any>>('rec_item')!
|
||||||
|
const recDate = inject<Ref<any>>('rec_date')!
|
||||||
|
|
||||||
|
function confirm() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showConfirmVerify
|
||||||
|
recItem.value = props.rec
|
||||||
|
recDate.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button type="button" variant="outline" class="text-orange-500 border border-orange-400 bg-orange-50"
|
||||||
|
@click="confirm">
|
||||||
|
<Icon name="i-lucide-circle-check" class="h-4 w-4 align-middle transition-colors" />
|
||||||
|
Konfirmasi
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ActionEvents, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||||
|
import Button from '~/components/pub/ui/button/Button.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
|
||||||
|
}>()
|
||||||
|
const isModalOpen = inject<Ref<boolean>>('isHistoryDialogOpen')!
|
||||||
|
|
||||||
|
function openDialog() {
|
||||||
|
isModalOpen.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button type="button" variant="outline" class="text-orange-500 border border-orange-400 bg-orange-50"
|
||||||
|
@click="openDialog">
|
||||||
|
<Icon name="i-lucide-history" class="h-4 w-4 align-middle transition-colors" />
|
||||||
|
History
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>S : </b></td>
|
||||||
|
<td>{{ props.rec.result.s }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Separator class="my-3" />
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>O : </b></td>
|
||||||
|
<td>{{ props.rec.result.o }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Separator class="my-3" />
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>A : </b></td>
|
||||||
|
<td>{{ props.rec.result.a }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Separator class="my-3" />
|
||||||
|
<div>
|
||||||
|
<h1><b>P : </b></h1>
|
||||||
|
<ul class="pl-5 list-disc space-y-1">
|
||||||
|
<li>
|
||||||
|
<h1><b>Goal of Treatment</b></h1>
|
||||||
|
<p>{{ props.rec.result.p.goal }}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h1><b>Tindakan/Program Rehabilitasi Medik</b></h1>
|
||||||
|
<p>{{ props.rec.result.p.action }}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h1><b>Edukasi</b></h1>
|
||||||
|
<p>{{ props.rec.result.p.education }}</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h1><b>Frekuensi Kunjungan</b></h1>
|
||||||
|
<p>{{ props.rec.result.p.frequency }} x Perminggu</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Separator class="my-3" />
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>Rencana Tindak Lanjut : </b></td>
|
||||||
|
<td>{{ props.rec.result.plan }} - {{ props.rec.result.planDesc }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ActionEvents, type LinkItem, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: ListItemDto
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { getActiveRole } = useUserStore()
|
||||||
|
const recId = inject<Ref<number>>('rec_id')!
|
||||||
|
const recAction = inject<Ref<string>>('rec_action')!
|
||||||
|
const recItem = inject<Ref<any>>('rec_item')!
|
||||||
|
const timestamp = inject<Ref<any>>('timestamp')!
|
||||||
|
|
||||||
|
const activeKey = ref<string | null>(null)
|
||||||
|
const linkItems = computed(() => {
|
||||||
|
const role = getActiveRole()
|
||||||
|
const isAdmin = role == "system"
|
||||||
|
const isDoctorRole = role == "emp|doc"
|
||||||
|
const isPhysioRole = role == "emp|doc"
|
||||||
|
const isUnverified = true // recItem.id === 0
|
||||||
|
const isUnvalidated = true // recItem.id
|
||||||
|
|
||||||
|
const items: LinkItem[] = [
|
||||||
|
{ label: 'Print', onClick: print, icon: 'i-lucide-printer', }
|
||||||
|
]
|
||||||
|
if (isDoctorRole || isAdmin) {
|
||||||
|
items.push({ label: 'Edit', onClick: edit, icon: 'i-lucide-pencil', })
|
||||||
|
|
||||||
|
if (isUnverified) {
|
||||||
|
items.push({ label: 'Verify', onClick: verify, icon: 'i-lucide-check', })
|
||||||
|
}
|
||||||
|
if (!isUnverified && isUnvalidated) { // verified & unvalidated
|
||||||
|
items.push({ label: 'Validate', onClick: validate, icon: 'i-lucide-check-check', })
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({ label: 'Delete', onClick: del, icon: 'i-lucide-trash', })
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
})
|
||||||
|
|
||||||
|
function edit() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showEdit
|
||||||
|
recItem.value = props.rec
|
||||||
|
timestamp.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showVerify
|
||||||
|
recItem.value = props.rec
|
||||||
|
timestamp.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
function validate() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showValidate
|
||||||
|
recItem.value = props.rec
|
||||||
|
timestamp.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
function print() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showPrint
|
||||||
|
recItem.value = props.rec
|
||||||
|
timestamp.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
function del() {
|
||||||
|
recId.value = props.rec.id || 0
|
||||||
|
recAction.value = ActionEvents.showConfirmDelete
|
||||||
|
recItem.value = props.rec
|
||||||
|
timestamp.value = new Date().getTime()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<SidebarMenuButton size="lg"
|
||||||
|
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white dark:data-[state=open]:bg-slate-800">
|
||||||
|
<Icon name="i-lucide-chevrons-up-down" class="ml-auto size-4" />
|
||||||
|
</SidebarMenuButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
class="w-[--radix-dropdown-menu-trigger-width] min-w-40 rounded-lg border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
|
||||||
|
align="end">
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem v-for="item in linkItems" :key="item.label"
|
||||||
|
class="hover:bg-gray-100 dark:hover:bg-slate-700" @click="item.onClick" @mouseenter="activeKey = item.label"
|
||||||
|
@mouseleave="activeKey = null">
|
||||||
|
<Icon :name="item.icon ?? ''" />
|
||||||
|
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { Label as RadioLabel } from '~/components/pub/ui/label'
|
||||||
|
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
fieldName?: string
|
||||||
|
label?: string
|
||||||
|
errors?: FormErrors
|
||||||
|
class?: string
|
||||||
|
radioGroupClass?: string
|
||||||
|
radioItemClass?: string
|
||||||
|
labelClass?: string
|
||||||
|
isRequired?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const {
|
||||||
|
fieldName = 'isNewBorn',
|
||||||
|
label = 'Status Pasien',
|
||||||
|
errors,
|
||||||
|
class: containerClass,
|
||||||
|
radioGroupClass,
|
||||||
|
radioItemClass,
|
||||||
|
labelClass,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const newbornOptions = [
|
||||||
|
{ value: 'EVALUASI', label: 'Evaluasi' },
|
||||||
|
{ value: 'RUJUK', label: 'Rujuk' },
|
||||||
|
{ value: 'SELESAI', label: 'Selesai' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DE.Cell :class="cn('radio-group-field', containerClass)" :col-span="2">
|
||||||
|
<DE.Label
|
||||||
|
:label-for="fieldName"
|
||||||
|
:is-required="isRequired"
|
||||||
|
>
|
||||||
|
{{ label }}
|
||||||
|
</DE.Label>
|
||||||
|
<DE.Field
|
||||||
|
:id="fieldName"
|
||||||
|
:errors="errors"
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
:name="fieldName"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
v-bind="componentField"
|
||||||
|
:class="cn('flex flex-row flex-wrap gap-4 sm:gap-6', radioGroupClass)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(option, index) in newbornOptions"
|
||||||
|
:key="option.value"
|
||||||
|
:class="cn('flex min-w-fit items-center space-x-2 pt-1', radioItemClass)"
|
||||||
|
>
|
||||||
|
<RadioGroupItem
|
||||||
|
:id="`${fieldName}-${index}`"
|
||||||
|
:value="option.value"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'relative h-4 w-4 rounded-full border-muted-foreground before:absolute before:inset-1 before:rounded-full before:bg-primary before:opacity-0 before:transition-opacity data-[state=checked]:border-primary data-[state=checked]:bg-white data-[state=checked]:before:opacity-100 sm:h-5 sm:w-5',
|
||||||
|
containerClass,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<RadioLabel
|
||||||
|
:for="`${fieldName}-${index}`"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'cursor-pointer select-none text-xs font-normal leading-none transition-colors hover:text-primary peer-disabled:cursor-not-allowed peer-disabled:opacity-70 sm:text-sm',
|
||||||
|
labelClass,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</RadioLabel>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage class="ml-0 mt-1" />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</DE.Field>
|
||||||
|
</DE.Cell>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Badge } from '~/components/pub/ui/badge'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
rec: any
|
||||||
|
idx?: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const verifyStatusCodes: Record<string, string> = {
|
||||||
|
verified: 'Terverifikasi',
|
||||||
|
unverified: 'Belum Verifikasi',
|
||||||
|
}
|
||||||
|
const validateStatusCodes: Record<string, string> = {
|
||||||
|
validated: 'Tervalidasi',
|
||||||
|
unvalidated: 'Belum Validasi',
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyStatusText = computed(() => {
|
||||||
|
const code: keyof typeof verifyStatusCodes = props.rec.status.verified === 1 ? `verified` : `unverified`
|
||||||
|
return verifyStatusCodes[code]
|
||||||
|
})
|
||||||
|
const validateStatusText = computed(() => {
|
||||||
|
const code: keyof typeof validateStatusCodes = props.rec.status.validated === 1 ? `validated` : `unvalidated`
|
||||||
|
return validateStatusCodes[code]
|
||||||
|
})
|
||||||
|
|
||||||
|
const verifyBadgeVariant = computed(() => {
|
||||||
|
return props.rec.status.verified === 1 ? 'default' : 'outline'
|
||||||
|
})
|
||||||
|
const validateBadgeVariant = computed(() => {
|
||||||
|
return props.rec.status.validated === 1 ? 'default' : 'outline'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 items-center justify-center">
|
||||||
|
<Badge :variant="verifyBadgeVariant" class="w-fit rounded-2xl text-[0.6rem]" >
|
||||||
|
{{ verifyStatusText }}
|
||||||
|
</Badge>
|
||||||
|
<Badge :variant="validateBadgeVariant" class="w-fit rounded-2xl text-[0.6rem]" >
|
||||||
|
{{ validateStatusText }}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
|
||||||
|
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||||
|
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import RadioFollowup from './_common/radio-followup.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
schema: any
|
||||||
|
initialValues?: any
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
// const isMedicalDiagnosisPickerDialogOpen = ref<boolean>(false)
|
||||||
|
// const isFunctionalDiagnosisPickerDialogOpen = ref<boolean>(false)
|
||||||
|
// const isProcedurePickerDialogOpen = ref<boolean>(false)
|
||||||
|
|
||||||
|
// function toggleMedicalDiagnosisPickerDialog() {
|
||||||
|
// isMedicalDiagnosisPickerDialogOpen.value = !isMedicalDiagnosisPickerDialogOpen.value
|
||||||
|
// }
|
||||||
|
// function toggleFunctionalDiagnosisPickerDialog() {
|
||||||
|
// isFunctionalDiagnosisPickerDialogOpen.value = !isFunctionalDiagnosisPickerDialogOpen.value
|
||||||
|
// }
|
||||||
|
|
||||||
|
// provide(`isDiagnosisPickerDialogOpen`, isDiagnosisPickerDialogOpen)
|
||||||
|
// provide(`isProcedurePickerDialogOpen`, isProcedurePickerDialogOpen)
|
||||||
|
|
||||||
|
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">
|
||||||
|
|
||||||
|
<!-- FORM 1 -->
|
||||||
|
<DE.Block :col-count="2" :cell-flex="false">
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="subjective"
|
||||||
|
label="Subjective"
|
||||||
|
placeholder="Subjective"
|
||||||
|
class="w-1/2"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell :col-span="2" >
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="objective"
|
||||||
|
label="Objective"
|
||||||
|
placeholder="Masukkan Objective"
|
||||||
|
class="w-1/2"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="assesment"
|
||||||
|
label="Assesment"
|
||||||
|
placeholder="Masukkan Assesment"
|
||||||
|
class="w-1/2"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
<DE.Cell class="mt-2 px-4 bg-gray-50 border rounded-lg" :col-span="2">
|
||||||
|
<DE.Block :col-count="2" :cell-flex="false">
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="planningGoal"
|
||||||
|
label="Goal of Treatment"
|
||||||
|
placeholder="Masukkan Goal of Treatment"
|
||||||
|
:errors="errors" />
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="planningAction"
|
||||||
|
label="Tindakan/Program Rehabilitasi Medik"
|
||||||
|
placeholder="Masukkan Tindakan/Program Rehabilitasi Medik"
|
||||||
|
:errors="errors" />
|
||||||
|
<TextAreaInput
|
||||||
|
field-name="planningEducation"
|
||||||
|
label="Edukasi"
|
||||||
|
placeholder="Masukkan Edukasi"
|
||||||
|
:errors="errors" />
|
||||||
|
<InputBase
|
||||||
|
field-name="planningFrequency"
|
||||||
|
label="Frekuensi Kunjungan"
|
||||||
|
right-label="x Minggu"
|
||||||
|
placeholder="Masukkan Frekuensi Kunjungan"
|
||||||
|
:errors="errors"
|
||||||
|
numeric-only
|
||||||
|
is-disabled
|
||||||
|
/>
|
||||||
|
</DE.Block>
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
<DE.Cell :col-span="2">
|
||||||
|
<RadioFollowup
|
||||||
|
field-name="followUpPlan"
|
||||||
|
label="Rencana Tindak Lanjut"
|
||||||
|
:errors="errors"
|
||||||
|
is-required
|
||||||
|
/>
|
||||||
|
<TextAreaInput
|
||||||
|
label=""
|
||||||
|
field-name="followUpPlanDesc"
|
||||||
|
placeholder="Masukkan Keterangan rencana tindak lanjut"
|
||||||
|
class="w-1/2 mt-3"
|
||||||
|
:errors="errors" />
|
||||||
|
</DE.Cell>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</DE.Block>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||||
|
import type { Patient } from '~/models/patient'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dp.vue'))
|
||||||
|
const resultData = defineAsyncComponent(() => import('./_common/card-result.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./_common/verify-badge.vue'))
|
||||||
|
|
||||||
|
export const config: Config = {
|
||||||
|
cols: [{}, { width: 800 }, {}, { width: 120 }, { width: 3 },],
|
||||||
|
|
||||||
|
headers: [
|
||||||
|
[
|
||||||
|
{ label: 'Tanggal' },
|
||||||
|
{ label: 'Hasil Asesmen Pasien Dan Pemberian Pelayanan' },
|
||||||
|
{ label: 'Jenis Form' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: 'Action' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
keys: ['date', 'result', 'type', 'status', 'action'],
|
||||||
|
|
||||||
|
parses: {
|
||||||
|
date: (rec: unknown): unknown => {
|
||||||
|
const date = (rec as any).date
|
||||||
|
|
||||||
|
if (typeof date == 'object' && date) {
|
||||||
|
return (date as Date).toLocaleDateString('id-ID')
|
||||||
|
} else if (typeof date == 'string') {
|
||||||
|
return (date as string).substring(0, 10)
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
result(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: resultData,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||||
|
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||||
|
import { config } from './history-list.cfg'
|
||||||
|
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
paginationMeta: PaginationMeta
|
||||||
|
dateValue: DateRange
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const df = new DateFormatter('en-US', { dateStyle: 'medium',})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
pageChange: [page: number]
|
||||||
|
'update:dateValue': [value: DateRange]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
emit('pageChange', page)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger as-child>
|
||||||
|
<Button variant="outline" :class="cn('mb-1 w-[280px] justify-start text-left font-normal',
|
||||||
|
!props.dateValue && 'text-muted-foreground')">
|
||||||
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||||
|
<template v-if="props.dateValue.start">
|
||||||
|
<template v-if="props.dateValue.end">
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }} -
|
||||||
|
{{ df.format(props.dateValue.end.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
{{ df.format(props.dateValue.start.toDate(getLocalTimeZone())) }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else> Pick a date </template>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="w-auto p-0">
|
||||||
|
<RangeCalendar v-model="props.dateValue" initial-focus :number-of-months="2"
|
||||||
|
@update:model-value="(date) => emit('update:dateValue', date)" />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<PubMyUiDataTable
|
||||||
|
v-bind="config"
|
||||||
|
:rows="data"
|
||||||
|
:skeleton-size="paginationMeta?.pageSize"
|
||||||
|
/>
|
||||||
|
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
const action = defineAsyncComponent(() => import('./_common/dropdown-action.vue'))
|
||||||
|
const statusBadge = defineAsyncComponent(() => import('./_common/verify-badge.vue'))
|
||||||
|
const resultData = defineAsyncComponent(() => import('./_common/card-result.vue'))
|
||||||
|
|
||||||
|
export const config: Config = {
|
||||||
|
cols: [{}, { width: 800 }, {}, { width: 120 }, { width: 3 },],
|
||||||
|
|
||||||
|
headers: [
|
||||||
|
[
|
||||||
|
{ label: 'Tanggal' },
|
||||||
|
{ label: 'Hasil Asesmen Pasien Dan Pemberian Pelayanan' },
|
||||||
|
{ label: 'Jenis Form' },
|
||||||
|
{ label: 'Status' },
|
||||||
|
{ label: 'Action' },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
keys: ['date', 'result', 'type', 'status', 'action'],
|
||||||
|
|
||||||
|
parses: {
|
||||||
|
date: (rec: unknown): unknown => {
|
||||||
|
const date = (rec as any).date
|
||||||
|
|
||||||
|
if (typeof date == 'object' && date) {
|
||||||
|
return (date as Date).toLocaleDateString('id-ID')
|
||||||
|
} else if (typeof date == 'string') {
|
||||||
|
return (date as string).substring(0, 10)
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
result(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: resultData,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
action(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
status(rec, idx) {
|
||||||
|
return {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: statusBadge,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { config } from './list.cfg'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: any[]
|
||||||
|
}
|
||||||
|
defineProps<Props>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<PubMyUiDataTable v-bind="config" :rows="data" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { FormErrors } from '~/types/error'
|
||||||
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
|
import FieldGroup from '~/components/pub/my-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/my-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/my-ui/form/label.vue'
|
||||||
|
import Select from '~/components/pub/my-ui/form/select.vue'
|
||||||
|
import { Form } from '~/components/pub/ui/form'
|
||||||
|
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||||
|
import type { InstallationFormData } from '~/schemas/installation.schema'
|
||||||
|
import TextCaptcha from '~/components/pub/my-ui/form/text-captcha.vue'
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
schema: any
|
||||||
|
errors?: FormErrors
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: InstallationFormData, resetForm: () => void]
|
||||||
|
cancel: [resetForm: () => void]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formSchema = toTypedSchema(props.schema)
|
||||||
|
const formRef = ref()
|
||||||
|
const captchaRef = ref<InstanceType<typeof TextCaptcha> | null>(null)
|
||||||
|
const captchaValid = ref(false)
|
||||||
|
|
||||||
|
// Form submission handler
|
||||||
|
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||||
|
const formData: InstallationFormData = {
|
||||||
|
name: values.name || '',
|
||||||
|
code: values.code || '',
|
||||||
|
}
|
||||||
|
emit('submit', formData, resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCaptchaUpdate(valid: boolean) {
|
||||||
|
captchaValid.value = valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form cancel handler
|
||||||
|
function onCancelForm({ resetForm }: { resetForm: () => void }) {
|
||||||
|
emit('cancel', resetForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
validation-mode="onSubmit"
|
||||||
|
>
|
||||||
|
<div class="border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
|
||||||
|
<InputBase
|
||||||
|
field-name="name"
|
||||||
|
label="Nama"
|
||||||
|
placeholder="Masukkan Nama"
|
||||||
|
:errors="errors"/>
|
||||||
|
<InputBase
|
||||||
|
field-name="email"
|
||||||
|
label="Email"
|
||||||
|
placeholder="Masukkan Email"
|
||||||
|
:errors="errors"/>
|
||||||
|
|
||||||
|
<div class="mt-2">
|
||||||
|
<Label class="" for="password">Password</Label>
|
||||||
|
<Field class="" id="password" :errors="errors">
|
||||||
|
<FormField v-slot="{ componentField }" name="password">
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
v-bind="componentField"
|
||||||
|
type="password"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
</Field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TextCaptcha
|
||||||
|
ref="captchaRef"
|
||||||
|
:length="5"
|
||||||
|
:useSpacing="true"
|
||||||
|
:noiseChars="true"
|
||||||
|
@update:valid="onCaptchaUpdate"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import type { ExposedForm } from '~/types/form'
|
||||||
|
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
|
||||||
|
import { KfrSchema, } from '~/schemas/kfr.schema'
|
||||||
|
import { handleActionSave, handleActionEdit } from '~/handlers/kfr.handler'
|
||||||
|
import { getDetail } from '~/services/kfr.service';
|
||||||
|
|
||||||
|
// #region Props & Emits
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
callbackUrl?: string
|
||||||
|
mode?: 'add' | 'edit'
|
||||||
|
}>(), {
|
||||||
|
mode: "add",
|
||||||
|
})
|
||||||
|
|
||||||
|
// form related state
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||||
|
const kfrId = typeof route.params.kfr_id == 'string' ? parseInt(route.params.kfr_id) : 0
|
||||||
|
const inputForm = ref<ExposedForm<any> | null>(null)
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region State & Computed
|
||||||
|
const router = useRouter()
|
||||||
|
const isConfirmationOpen = ref(false)
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Lifecycle Hooks
|
||||||
|
onMounted(() => {
|
||||||
|
if(props.mode === `edit`) init()
|
||||||
|
})
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Functions
|
||||||
|
async function init(){
|
||||||
|
const result = await getDetail(kfrId)
|
||||||
|
if (result.success) {
|
||||||
|
const currentValue = result.body?.data || {}
|
||||||
|
inputForm.value?.setValues(currentValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack() {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirmAdd() {
|
||||||
|
const inputData: any = await composeFormData()
|
||||||
|
const response = props.mode === `add`
|
||||||
|
? await handleActionSave(
|
||||||
|
inputData,
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
|
toast,
|
||||||
|
)
|
||||||
|
: await handleActionEdit(
|
||||||
|
kfrId,
|
||||||
|
inputData,
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
|
toast,
|
||||||
|
)
|
||||||
|
|
||||||
|
const data = (response?.body?.data ?? null)
|
||||||
|
if (!data) return
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function composeFormData(): Promise<any> {
|
||||||
|
const [input,] = await Promise.all([
|
||||||
|
inputForm.value?.validate(),
|
||||||
|
])
|
||||||
|
|
||||||
|
const results = [input]
|
||||||
|
const allValid = results.every((r) => r?.valid)
|
||||||
|
|
||||||
|
// exit, if form errors happend during validation
|
||||||
|
if (!allValid) return Promise.reject('Form validation failed')
|
||||||
|
|
||||||
|
const formData = input?.values
|
||||||
|
formData.encounter_id = encounterId
|
||||||
|
|
||||||
|
return new Promise((resolve) => resolve(formData))
|
||||||
|
}
|
||||||
|
// #endregion region
|
||||||
|
|
||||||
|
// #region Utilities & event handlers
|
||||||
|
async function handleActionClick(eventType: string) {
|
||||||
|
if (eventType === 'submit') {
|
||||||
|
isConfirmationOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'back') {
|
||||||
|
if (props.callbackUrl) return navigateTo(props.callbackUrl)
|
||||||
|
goBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelAdd() {
|
||||||
|
isConfirmationOpen.value = false
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Watchers
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
const initial = {
|
||||||
|
// subjective: '',
|
||||||
|
// objective: '',
|
||||||
|
// assesment: '',
|
||||||
|
// planningGoal: '',
|
||||||
|
// planningAction: '',
|
||||||
|
// planningEducation: '',
|
||||||
|
planningFrequency: 2,
|
||||||
|
followUpPlan: 'EVALUASI',
|
||||||
|
// followUpPlanDesc: '',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">
|
||||||
|
{{ props.mode === "add" ? `Tambah` : `Update` }} Formulir Rawat Jalan KFR
|
||||||
|
</div>
|
||||||
|
<AppKfrEntry
|
||||||
|
ref="inputForm"
|
||||||
|
:schema="KfrSchema"
|
||||||
|
:initial-values="initial"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<Action :enable-draft="false" @click="handleActionClick"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Confirmation
|
||||||
|
v-model:open="isConfirmationOpen"
|
||||||
|
title="Simpan Data"
|
||||||
|
message="Apakah Anda yakin ingin menyimpan data ini?"
|
||||||
|
confirm-text="Simpan"
|
||||||
|
@confirm="handleConfirmAdd"
|
||||||
|
@cancel="handleCancelAdd"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
// #region Imports
|
||||||
|
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||||
|
|
||||||
|
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||||
|
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||||
|
|
||||||
|
import { getList, remove } from '~/services/kfr.service'
|
||||||
|
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||||
|
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
import type { Encounter } from '~/models/encounter'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import type { ExposedForm } from '~/types/form'
|
||||||
|
import { VerificationSchema } from '~/schemas/verification.schema'
|
||||||
|
import { handleActionSave } from '~/handlers/kfr.handler'
|
||||||
|
import { toast } from '~/components/pub/ui/toast'
|
||||||
|
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||||
|
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||||
|
import { CalendarDate } from '@internationalized/date'
|
||||||
|
import type { DateRange } from 'radix-vue'
|
||||||
|
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// Props
|
||||||
|
interface Props {
|
||||||
|
encounter: Encounter
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const route = useRoute()
|
||||||
|
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||||
|
const kfrId = typeof route.params.kfr_id == 'string' ? parseInt(route.params.kfr_id) : 0
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region State
|
||||||
|
const { getActiveRole } = useUserStore()
|
||||||
|
const isVerifyDialogOpen = ref(false)
|
||||||
|
const isValidateDialogOpen = ref(false)
|
||||||
|
const isHistoryDialogOpen = ref(false)
|
||||||
|
const isPatientInTherapy = ref(false)
|
||||||
|
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||||
|
fetchFn: (params) => getList({ ...params }),
|
||||||
|
entityName: 'kfr',
|
||||||
|
})
|
||||||
|
const historyData = usePaginatedList({
|
||||||
|
fetchFn: (params) => getList({ ...params }),
|
||||||
|
entityName: 'kfr-history',
|
||||||
|
})
|
||||||
|
|
||||||
|
const dummy = [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
date: new Date(),
|
||||||
|
result: {
|
||||||
|
s: `Example`,
|
||||||
|
o: `Example`,
|
||||||
|
a: `Example`,
|
||||||
|
p: {
|
||||||
|
goal: `Example`,
|
||||||
|
action: `Example`,
|
||||||
|
education: `Example`,
|
||||||
|
frequency: `Example`,
|
||||||
|
},
|
||||||
|
plan: `Example`,
|
||||||
|
planDesc: `Description`,
|
||||||
|
},
|
||||||
|
type: `Asesmen`,
|
||||||
|
status: {
|
||||||
|
verified: 1,
|
||||||
|
validated: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const isRecordConfirmationOpen = ref(false)
|
||||||
|
const isDocPreviewDialogOpen = ref(false)
|
||||||
|
const summaryLoading = ref(false)
|
||||||
|
|
||||||
|
const inputForm = ref<ExposedForm<any> | null>(null)
|
||||||
|
const recId = ref<number>(0)
|
||||||
|
const recAction = ref<string>('')
|
||||||
|
const recItem = ref<any>(null)
|
||||||
|
const timestamp = ref<number>(0)
|
||||||
|
|
||||||
|
const isAssessment = ref<boolean>(false)
|
||||||
|
const isTherapyProtocol = ref<boolean>(false)
|
||||||
|
const isReassessment = ref<boolean>(true)
|
||||||
|
const isInOrBeyondAssessmentPeriod = ref<boolean>(true)
|
||||||
|
|
||||||
|
const isDoctor = computed(() => getActiveRole() === 'emp|doc')
|
||||||
|
const isAdmin = computed(() => getActiveRole() === 'system')
|
||||||
|
|
||||||
|
const isCaptchaValid = ref(false)
|
||||||
|
provide('isCaptchaValid', isCaptchaValid)
|
||||||
|
|
||||||
|
const addBtnTxt = computed(() => {
|
||||||
|
if (isAssessment.value) {
|
||||||
|
return `Tambah Asesmen`
|
||||||
|
} else if (isTherapyProtocol.value) {
|
||||||
|
return `Tambah Protokol Terapi`
|
||||||
|
} else if (isReassessment.value) {
|
||||||
|
return `Tambah Re-Asesmen`
|
||||||
|
}
|
||||||
|
return `Tambah Asesmen`
|
||||||
|
})
|
||||||
|
|
||||||
|
const headerPrep: HeaderPrep = {
|
||||||
|
title: "Formulir Rawat Jalan KFR",
|
||||||
|
icon: 'i-lucide-newspaper',
|
||||||
|
}
|
||||||
|
if(isDoctor.value || isAdmin.value) {
|
||||||
|
headerPrep.addNav = {
|
||||||
|
label: addBtnTxt.value,
|
||||||
|
onClick: () => navigateTo({
|
||||||
|
name: 'rehab-encounter-id-kfr-add',
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!isAssessment.value) {
|
||||||
|
headerPrep.components = [
|
||||||
|
{
|
||||||
|
component: defineAsyncComponent(() => import('~/components/app/kfr/_common/btn-history.vue')),
|
||||||
|
props: { }
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const defaultDate = {
|
||||||
|
start: new CalendarDate(2025, 1, 20),
|
||||||
|
end: new CalendarDate(2025, 1, 20).add({ days: 20 }),
|
||||||
|
}
|
||||||
|
const historyDateValue = ref(defaultDate) as Ref<DateRange>
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Lifecycle Hooks
|
||||||
|
onMounted(() => {
|
||||||
|
getPatientSummary()
|
||||||
|
|
||||||
|
const isInTherapy = false // TODO: determine if patient is in therapy
|
||||||
|
handleIsPatientInTherapy(isInTherapy)
|
||||||
|
})
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Functions
|
||||||
|
function handleIsInAssesmentPeriood(value: boolean) {
|
||||||
|
if (value) {
|
||||||
|
isInOrBeyondAssessmentPeriod.value = true
|
||||||
|
} else {
|
||||||
|
isInOrBeyondAssessmentPeriod.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleIsPatientInTherapy(value: boolean) {
|
||||||
|
if (value) {
|
||||||
|
isPatientInTherapy.value = true
|
||||||
|
} else {
|
||||||
|
isPatientInTherapy.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleOpenHistory() {
|
||||||
|
isHistoryDialogOpen.value = true
|
||||||
|
}
|
||||||
|
async function getPatientSummary() {
|
||||||
|
try {
|
||||||
|
summaryLoading.value = true
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching patient summary:', error)
|
||||||
|
} finally {
|
||||||
|
summaryLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleHistoryDialog() {
|
||||||
|
isHistoryDialogOpen.value = !isHistoryDialogOpen.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVerify() {
|
||||||
|
isVerifyDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirmVerify() {
|
||||||
|
const inputData: any = await composeFormData()
|
||||||
|
const response = await handleActionSave(
|
||||||
|
inputData,
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
|
toast,
|
||||||
|
)
|
||||||
|
|
||||||
|
const data = (response?.body?.data ?? null)
|
||||||
|
if (!data) return
|
||||||
|
isVerifyDialogOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function composeFormData(): Promise<any> {
|
||||||
|
const [input,] = await Promise.all([
|
||||||
|
inputForm.value?.validate(),
|
||||||
|
])
|
||||||
|
|
||||||
|
const results = [input]
|
||||||
|
const allValid = results.every((r) => r?.valid)
|
||||||
|
|
||||||
|
// exit, if form errors happend during validation
|
||||||
|
if (!allValid) return Promise.reject('Form validation failed')
|
||||||
|
|
||||||
|
const formData = input?.values
|
||||||
|
// formData.encounter_id = encounterId
|
||||||
|
|
||||||
|
return new Promise((resolve) => resolve(formData))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirmValidate() {
|
||||||
|
try {
|
||||||
|
// const result = await remove(record.id)
|
||||||
|
// if (result.success) {
|
||||||
|
// toast({ title: 'Berhasil', description: 'Data berhasil dihapus', variant: 'default' })
|
||||||
|
// await fetchData()
|
||||||
|
// } else {
|
||||||
|
// toast({ title: 'Gagal', description: `Data gagal dihapus`, variant: 'destructive' })
|
||||||
|
// }
|
||||||
|
} catch (error) {
|
||||||
|
toast({ title: 'Gagal', description: `Something went wrong`, variant: 'destructive' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelValidate() {
|
||||||
|
// Reset record state when cancelled
|
||||||
|
recId.value = 0
|
||||||
|
recAction.value = ''
|
||||||
|
recItem.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleConfirmDelete(record: any, action: string) {
|
||||||
|
if (action === 'delete' && record?.id) {
|
||||||
|
try {
|
||||||
|
const result = await remove(record.id)
|
||||||
|
if (result.success) {
|
||||||
|
toast({ title: 'Berhasil', description: 'Data berhasil dihapus', variant: 'default' })
|
||||||
|
await fetchData()
|
||||||
|
} else {
|
||||||
|
toast({ title: 'Gagal', description: `Data gagal dihapus`, variant: 'destructive' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast({ title: 'Gagal', description: `Something went wrong`, variant: 'destructive' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelConfirmation() {
|
||||||
|
// Reset record state when cancelled
|
||||||
|
recId.value = 0
|
||||||
|
recAction.value = ''
|
||||||
|
recItem.value = null
|
||||||
|
}
|
||||||
|
// #endregion region
|
||||||
|
|
||||||
|
// #region Utilities & event handlers
|
||||||
|
async function handleActionClick(eventType: string) {
|
||||||
|
if (eventType === 'submit') {
|
||||||
|
handleConfirmVerify()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'back') {
|
||||||
|
isVerifyDialogOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Provide
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('timestamp', timestamp)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
provide('isHistoryDialogOpen', isHistoryDialogOpen)
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Watchers
|
||||||
|
watch([recId, recAction, timestamp], () => {
|
||||||
|
switch (recAction.value) {
|
||||||
|
case ActionEvents.showEdit:
|
||||||
|
// if(pagePermission.canUpdate) {
|
||||||
|
navigateTo({
|
||||||
|
name: 'rehab-encounter-id-kfr-kfr_id-edit',
|
||||||
|
params: {
|
||||||
|
kfr_id: kfrId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// } else {
|
||||||
|
// unauthorizedToast()
|
||||||
|
// }
|
||||||
|
break
|
||||||
|
case ActionEvents.showVerify:
|
||||||
|
// if(pagePermission.canUpdate) {
|
||||||
|
handleVerify()
|
||||||
|
// } else {
|
||||||
|
// unauthorizedToast()
|
||||||
|
// }
|
||||||
|
break
|
||||||
|
case ActionEvents.showValidate:
|
||||||
|
// if(pagePermission.canUpdate) {
|
||||||
|
isValidateDialogOpen.value = true
|
||||||
|
// } else {
|
||||||
|
// unauthorizedToast()
|
||||||
|
// }
|
||||||
|
break
|
||||||
|
case ActionEvents.showPrint:
|
||||||
|
isDocPreviewDialogOpen.value = true
|
||||||
|
break
|
||||||
|
case ActionEvents.showConfirmDelete:
|
||||||
|
isRecordConfirmationOpen.value = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endregion
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Header :prep="{ ...headerPrep }" />
|
||||||
|
|
||||||
|
<AppKfrCommonBannerPatientInTherapy v-if="isInOrBeyondAssessmentPeriod" class="mb-5" />
|
||||||
|
|
||||||
|
<AppKfrList :data="dummy" />
|
||||||
|
|
||||||
|
<Dialog v-model:open="isVerifyDialogOpen" title="Verifikasi">
|
||||||
|
<AppKfrVerifyDialog ref="inputForm" :schema="VerificationSchema" />
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<Action v-show="isCaptchaValid" :enable-draft="false" @click="handleActionClick" />
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Confirmation
|
||||||
|
v-model:open="isValidateDialogOpen"
|
||||||
|
title="Validasi Data"
|
||||||
|
message="Apakah Anda yakin ingin Validasi data ini?"
|
||||||
|
confirm-text="Simpan"
|
||||||
|
@confirm="handleConfirmValidate"
|
||||||
|
@cancel="handleCancelValidate"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dialog v-model:open="isHistoryDialogOpen" title="History" size="full">
|
||||||
|
<AppKfrHistoryList
|
||||||
|
:data="dummy"
|
||||||
|
v-model:date-value="historyDateValue"
|
||||||
|
:pagination-meta="paginationMeta"
|
||||||
|
@page-change="handlePageChange" />
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<RecordConfirmation v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem"
|
||||||
|
@confirm="handleConfirmDelete" @cancel="handleCancelConfirmation">
|
||||||
|
<template #default="{ record }">
|
||||||
|
<div class="text-sm">
|
||||||
|
<p>
|
||||||
|
<strong>ID:</strong>
|
||||||
|
{{ record?.id }}
|
||||||
|
</p>
|
||||||
|
<p v-if="record?.firstName">
|
||||||
|
<strong>Nama:</strong>
|
||||||
|
{{ record.firstName }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RecordConfirmation>
|
||||||
|
|
||||||
|
<Dialog v-model:open="isDocPreviewDialogOpen" title="Preview Dokumen" size="2xl">
|
||||||
|
<!-- <DocPreviewDialog :link="recItem.url" /> -->
|
||||||
|
<DocPreviewDialog :link="`https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf`" />
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
@@ -87,6 +87,7 @@ export const ActionEvents = {
|
|||||||
showProcess: 'showProcess',
|
showProcess: 'showProcess',
|
||||||
showCancel: 'showCancel',
|
showCancel: 'showCancel',
|
||||||
showVerify: 'showVerify',
|
showVerify: 'showVerify',
|
||||||
|
showConfirmVerify: 'showConfirmVerify',
|
||||||
showValidate: 'showValidate',
|
showValidate: 'showValidate',
|
||||||
showConfirmVerify: 'showConfirmVerify',
|
showConfirmVerify: 'showConfirmVerify',
|
||||||
showPrint: 'showPrint',
|
showPrint: 'showPrint',
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ function handleInput(event: Event) {
|
|||||||
<template>
|
<template>
|
||||||
<DE.Cell :col-span="colSpan || 1">
|
<DE.Cell :col-span="colSpan || 1">
|
||||||
<DE.Label
|
<DE.Label
|
||||||
class="mb-1"
|
class="font-medium mb-1"
|
||||||
v-if="label !== ''"
|
v-if="label !== ''"
|
||||||
:label-for="fieldName"
|
:label-for="fieldName"
|
||||||
:is-required="isRequired && !isDisabled"
|
:is-required="isRequired && !isDisabled"
|
||||||
@@ -69,7 +69,7 @@ function handleInput(event: Event) {
|
|||||||
v-bind="componentField"
|
v-bind="componentField"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0')"
|
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0', props.class)"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
aria-autocomplete="none"
|
aria-autocomplete="none"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ const isOpen = computed({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:open="isOpen">
|
<Dialog v-model:open="isOpen">
|
||||||
<DialogContent
|
<DialogContent :class="sizeClass" @interact-outside="(e: any) => preventOutside && e.preventDefault()"
|
||||||
:class="sizeClass"
|
@pointer-down-outside="(e: any) => preventOutside && e.preventDefault()">
|
||||||
@interact-outside="(e: any) => preventOutside && e.preventDefault()"
|
|
||||||
@pointer-down-outside="(e: any) => preventOutside && e.preventDefault()"
|
|
||||||
>
|
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle :class="`text-sm 2xl:text-base font-semibold flex ${titleClass || ''}`">
|
<DialogTitle :class="`text-sm 2xl:text-base font-semibold flex ${titleClass || ''}`">
|
||||||
<div v-if="props.titleIcon" class="me-2 pt-0.5">
|
<div class="me-2 pt-0.5">
|
||||||
<Icon :name="props.titleIcon" :class="`!pt-2`" />
|
<Icon v-if="props.titleIcon" :name="props.titleIcon" :class="`!pt-2`" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ props.title }}
|
{{ props.title }}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function onClick(type: ClickType) {
|
|||||||
<div :class="`${defaultClass} ${additionalClass} ${btnClass}`">
|
<div :class="`${defaultClass} ${additionalClass} ${btnClass}`">
|
||||||
<div>
|
<div>
|
||||||
<Button variant="ghost" type="button" @click="onClick('back')">
|
<Button variant="ghost" type="button" @click="onClick('back')">
|
||||||
<Icon name="i-lucide-arrow-left" />
|
<Icon name="i-lucide-arrow-left" />
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +38,7 @@ function onClick(type: ClickType) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button type="button" @click="onClick('submit')">
|
<Button type="button" @click="onClick('submit')">
|
||||||
<Icon name="i-lucide-check" />
|
<Icon name="i-lucide-check" />
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Handlers
|
||||||
|
import { genCrudHandler } from '~/handlers/_handler'
|
||||||
|
|
||||||
|
// Services
|
||||||
|
import { create, update, remove } from '~/services/kfr.service'
|
||||||
|
|
||||||
|
export const {
|
||||||
|
recId,
|
||||||
|
recAction,
|
||||||
|
recItem,
|
||||||
|
isReadonly,
|
||||||
|
isProcessing,
|
||||||
|
isFormEntryDialogOpen,
|
||||||
|
isRecordConfirmationOpen,
|
||||||
|
onResetState,
|
||||||
|
handleActionSave,
|
||||||
|
handleActionEdit,
|
||||||
|
handleActionRemove,
|
||||||
|
handleCancelForm,
|
||||||
|
} = genCrudHandler({
|
||||||
|
create,
|
||||||
|
update,
|
||||||
|
remove,
|
||||||
|
})
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<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: 'Protokol Terapi',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||||
|
|
||||||
|
const { checkRole, hasCreateAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
// if (!hasAccess) {
|
||||||
|
// throw createError({
|
||||||
|
// statusCode: 403,
|
||||||
|
// statusMessage: 'Access denied',
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canCreate = true // hasCreateAccess(roleAccess)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canCreate">
|
||||||
|
<ContentKfrEntry mode="edit" />
|
||||||
|
</div>
|
||||||
|
<Error
|
||||||
|
v-else
|
||||||
|
:status-code="403"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<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: 'Protokol Terapi',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => `${route.meta.title}`, // backtick to avoid the ts-plugin(2322) warning
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||||
|
|
||||||
|
const { checkRole, hasCreateAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
// if (!hasAccess) {
|
||||||
|
// throw createError({
|
||||||
|
// statusCode: 403,
|
||||||
|
// statusMessage: 'Access denied',
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canCreate = true // hasCreateAccess(roleAccess)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canCreate">
|
||||||
|
<ContentKfrEntry />
|
||||||
|
</div>
|
||||||
|
<Error
|
||||||
|
v-else
|
||||||
|
:status-code="403"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
type KfrFormData = z.infer<typeof KfrSchema>
|
||||||
|
const KfrSchema = z.object({
|
||||||
|
subjective: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
objective: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
assesment: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
planningGoal: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
planningAction: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
planningEducation: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
planningFrequency: z.number({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
followUpPlan: z.enum(['EVALUASI', 'RUJUK', "SELESAI"], {required_error: 'Mohon lengkapi status pasien', }),
|
||||||
|
followUpPlanDesc: z.string({required_error: 'Mohon lengkapi Form',}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export { KfrSchema, }
|
||||||
|
export type { KfrFormData, }
|
||||||
@@ -16,4 +16,4 @@ const VerificationSchema = z.object({
|
|||||||
type VerificationFormData = z.infer<typeof VerificationSchema>
|
type VerificationFormData = z.infer<typeof VerificationSchema>
|
||||||
|
|
||||||
export { VerificationSchema, }
|
export { VerificationSchema, }
|
||||||
export type { VerificationFormData, }
|
export type { VerificationFormData, }
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Base
|
||||||
|
import * as base from './_crud-base'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
const path = '/api/v1/kfr'
|
||||||
|
const name = 'kfr'
|
||||||
|
|
||||||
|
export function create(data: any) {
|
||||||
|
return base.create(path, data, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getList(params: any = null) {
|
||||||
|
return base.getList(path, params, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDetail(id: number | string, params?: any) {
|
||||||
|
return base.getDetail(path, id, name, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
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