525 lines
13 KiB
Vue
525 lines
13 KiB
Vue
<script setup lang="ts">
|
|
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
|
|
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
|
|
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
|
|
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
|
|
|
|
// Helpers
|
|
import type z from 'zod'
|
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
import { useForm } from 'vee-validate'
|
|
import { genBase } from '~/models/_base'
|
|
|
|
interface Masalah {
|
|
id: number
|
|
date: string
|
|
diagnosa: string
|
|
finishDate: string
|
|
staff: string
|
|
}
|
|
|
|
const props = defineProps<{
|
|
modelValue: any
|
|
schema: z.ZodSchema<any>
|
|
excludeFields?: string[]
|
|
isReadonly?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', val: any): void
|
|
(e: 'submit', val: any): void
|
|
}>()
|
|
|
|
// Setup form
|
|
const {
|
|
validate: _validate,
|
|
defineField,
|
|
handleSubmit,
|
|
errors,
|
|
values,
|
|
} = useForm({
|
|
validationSchema: toTypedSchema(props.schema),
|
|
initialValues: props.modelValue,
|
|
})
|
|
|
|
watch(values, (val) => emit('update:modelValue', val), { deep: true })
|
|
|
|
// Subjective
|
|
const [primaryComplaint, primaryComplaintAttrs] = defineField('pri-complain')
|
|
const [medType, medTypeAttrs] = defineField('med-type')
|
|
const [medName, medNameAttrs] = defineField('med-name')
|
|
const [medReaction, medReactionAttrs] = defineField('med-reaction')
|
|
const [foodType, foodTypeAttrs] = defineField('food-type')
|
|
const [foodName, foodNameAttrs] = defineField('food-name')
|
|
const [foodReaction, foodReactionAttrs] = defineField('food-reaction')
|
|
const [otherType, otherTypeAttrs] = defineField('other-type')
|
|
const [otherName, otherNameAttrs] = defineField('other-name')
|
|
const [otherReaction, otherReactionAttrs] = defineField('other-reaction')
|
|
const [painAsst, painAsstAttrs] = defineField('pain-asst')
|
|
const [painScale, painScaleAttrs] = defineField('pain-scale')
|
|
const [painTime, painTimeAttrs] = defineField('pain-time')
|
|
const [painDuration, painDurationAttrs] = defineField('pain-duration')
|
|
const [painFreq, painFreqAttrs] = defineField('pain-freq')
|
|
const [painLoc, painLocAttrs] = defineField('pain-loc')
|
|
const [nutScreening, nutScreeningAttrs] = defineField('nut-screening')
|
|
const [spiritualAsst, spiritualAsstAttrs] = defineField('spiritual-asst')
|
|
|
|
// Objective
|
|
const [generalCondition, generalConditionAttrs] = defineField('general-condition')
|
|
const [supportExam, supportExamAttrs] = defineField('support-exam')
|
|
const [riskFall, riskFallAttrs] = defineField('risk-fall')
|
|
const [bracelet, braceletAttrs] = defineField('bracelet')
|
|
const [braceletAlg, braceletAlgAttrs] = defineField('bracelet-alg')
|
|
|
|
const validate = async () => {
|
|
const result = await _validate()
|
|
console.log('Component validate() result:', result)
|
|
|
|
return {
|
|
valid: true,
|
|
data: result.values,
|
|
errors: result.errors,
|
|
}
|
|
}
|
|
|
|
const form = ref<Masalah>({
|
|
id: 0,
|
|
date: '',
|
|
diagnosa: '',
|
|
finishDate: '',
|
|
staff: '',
|
|
})
|
|
|
|
const list = ref<Masalah[]>([])
|
|
const isEditing = ref(false)
|
|
const showForm = ref(false)
|
|
|
|
const resetForm = () => {
|
|
form.value = {
|
|
id: 0,
|
|
date: '',
|
|
diagnosa: '',
|
|
finishDate: '',
|
|
staff: '',
|
|
}
|
|
isEditing.value = false
|
|
}
|
|
|
|
const openAdd = () => {
|
|
resetForm()
|
|
showForm.value = true
|
|
}
|
|
|
|
const submitForm = () => {
|
|
if (isEditing.value) {
|
|
const index = list.value.findIndex((v) => v.id === form.value.id)
|
|
if (index !== -1) list.value[index] = { ...form.value }
|
|
} else {
|
|
list.value.push({
|
|
...form.value,
|
|
id: Date.now(),
|
|
})
|
|
emit('click', { type: 'add-problem', data: list.value })
|
|
}
|
|
|
|
showForm.value = false
|
|
resetForm()
|
|
}
|
|
|
|
const editItem = (item: Masalah) => {
|
|
form.value = { ...item }
|
|
isEditing.value = true
|
|
showForm.value = true
|
|
}
|
|
|
|
const deleteItem = (id: number) => {
|
|
list.value = list.value.filter((v) => v.id !== id)
|
|
}
|
|
|
|
defineExpose({ validate })
|
|
const icdPreview = inject('icdPreview')
|
|
|
|
const isExcluded = (key: string) => props.excludeFields?.includes(key)
|
|
</script>
|
|
|
|
<template>
|
|
<form id="entry-form">
|
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
|
<div>
|
|
<h1 class="font-semibold">A. Data Subyektif</h1>
|
|
</div>
|
|
|
|
<div class="my-2">
|
|
<Block>
|
|
<Cell>
|
|
<Label dynamic>Keluhan Pasien</Label>
|
|
<Field :errMessage="errors['pri-complain']">
|
|
<Textarea
|
|
v-model="primaryComplaint"
|
|
v-bind="primaryComplaintAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
</div>
|
|
<div class="my-2 rounded-md border border-slate-300 p-4">
|
|
<span class="mb-4 text-sm font-semibold">Riwayat Alergi dan Reaksi Alergi</span>
|
|
<Block
|
|
:colCount="3"
|
|
class="mt-2"
|
|
>
|
|
<Cell>
|
|
<Label dynamic>a. Obat</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="medType"
|
|
v-bind="medTypeAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Sebutkan</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="medName"
|
|
v-bind="medNameAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Reaksi</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="medReaction"
|
|
v-bind="medReactionAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
<Block
|
|
:colCount="3"
|
|
class="mt-2"
|
|
>
|
|
<Cell>
|
|
<Label dynamic>b. Makanan</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="foodType"
|
|
v-bind="foodTypeAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Sebutkan</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="foodName"
|
|
v-bind="foodNameAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Reaksi</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="foodReaction"
|
|
v-bind="foodReactionAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
<Block
|
|
:colCount="3"
|
|
class="mt-2"
|
|
>
|
|
<Cell>
|
|
<Label dynamic>c. Lain-Lain</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="otherType"
|
|
v-bind="otherTypeAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
<Cell>
|
|
<Label dynamic>Sebutkan</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="otherName"
|
|
v-bind="otherNameAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
<Cell>
|
|
<Label dynamic>Reaksi</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="otherReaction"
|
|
v-bind="otherReactionAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
</div>
|
|
<div class="my-4">
|
|
<Block :colCount="3">
|
|
<Cell>
|
|
<Label dynamic>Kajian Nyeri</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="painAsst"
|
|
v-bind="painAsstAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Skala Nyeri</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="painScale"
|
|
v-bind="painScaleAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Waktu Nyeri</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="paintTime"
|
|
v-bind="painTimeAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
<Block :colCount="3">
|
|
<Cell>
|
|
<Label dynamic>Durasi Nyeri</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="painDuration"
|
|
v-bind="painDurationAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Frequensi Nyeri</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="painFreq"
|
|
v-bind="painFreqAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
<Cell>
|
|
<Label dynamic>Lokasi</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="painLoc"
|
|
v-bind="painLocAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
<Block :colCount="2">
|
|
<Cell>
|
|
<Label dynamic>Skrining Nutrisi</Label>
|
|
<Field>
|
|
<Textarea
|
|
v-model="nutScreening"
|
|
v-bind="nutScreeningAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
<Cell>
|
|
<Label dynamic>Kajian Spiritual</Label>
|
|
<Field>
|
|
<Textarea
|
|
v-model="spiritualAsst"
|
|
v-bind="spiritualAsstAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
</div>
|
|
|
|
<Separator class="mt-8" />
|
|
|
|
<div class="my-2">
|
|
<h1 class="font-semibold">B. Data Obyektif</h1>
|
|
</div>
|
|
|
|
<div class="my-2">
|
|
<Block :colCount="2">
|
|
<Cell>
|
|
<Label dynamic>Keadaan Umum</Label>
|
|
<Field>
|
|
<Textarea
|
|
v-model="generalCondition"
|
|
v-bind="generalConditionAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
<Cell>
|
|
<Label dynamic>Pemeriksaan Penunjang</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="supportExam"
|
|
v-bind="supportExamAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
<Block :colCount="3">
|
|
<Cell>
|
|
<Label dynamic>Risiko Jatuh</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="riskFall"
|
|
v-bind="riskFallAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Pemakaian Gelang</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="bracelet"
|
|
v-bind="braceletAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
|
|
<Cell>
|
|
<Label dynamic>Pasang Gelang Alergi</Label>
|
|
<Field>
|
|
<Input
|
|
v-model="braceletAlg"
|
|
v-bind="braceletAlgAttrs"
|
|
/>
|
|
</Field>
|
|
</Cell>
|
|
</Block>
|
|
</div>
|
|
|
|
<Separator class="mt-8" />
|
|
|
|
<div class="my-2">
|
|
<h1 class="my-3 font-semibold">C. Daftar Masalah Keperawatan</h1>
|
|
|
|
<Button
|
|
class="rounded bg-orange-100 px-3 py-1 text-orange-600"
|
|
type="button"
|
|
@click="showForm = true"
|
|
>
|
|
+ Tambah
|
|
</Button>
|
|
</div>
|
|
|
|
<div
|
|
v-if="showForm"
|
|
class="mb-4 space-y-3 rounded border bg-gray-50 p-4 shadow-sm"
|
|
>
|
|
<div>
|
|
<Label>Tanggal Muncul</Label>
|
|
<Input
|
|
v-model="form.date"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label class="font-medium">Diagnosa Keperawatan</Label>
|
|
<Input v-model="form.diagnosa" />
|
|
</div>
|
|
|
|
<div>
|
|
<Label class="font-medium">Tanggal Teratasi</Label>
|
|
<Input
|
|
v-model="form.finishDate"
|
|
type="date"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label class="font-medium">Nama Petugas</Label>
|
|
<Input v-model="form.staff" />
|
|
</div>
|
|
|
|
<div class="mt-2 flex gap-2">
|
|
<Button
|
|
@click="submitForm"
|
|
type="button"
|
|
>
|
|
{{ isEditing ? 'Update' : 'Tambah' }}
|
|
</Button>
|
|
|
|
<Button
|
|
@click="showForm = false"
|
|
type="button"
|
|
class="rounded bg-gray-300 px-3 py-1"
|
|
>
|
|
Batal
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-8">
|
|
<table class="w-full border text-sm">
|
|
<thead class="bg-gray-100">
|
|
<tr>
|
|
<th class="border p-2">Tanggal Muncul</th>
|
|
<th class="border p-2">Diagnosa</th>
|
|
<th class="border p-2">Tanggal Teratasi</th>
|
|
<th class="border p-2">Petugas</th>
|
|
<th class="w-28 border p-2">Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<tr
|
|
v-for="item in list"
|
|
:key="item.id"
|
|
class="border"
|
|
>
|
|
<td class="border p-2">{{ item.date }}</td>
|
|
<td class="border p-2">{{ item.diagnosa }}</td>
|
|
<td class="border p-2">{{ item.finishDate }}</td>
|
|
<td class="border p-2">{{ item.staff }}</td>
|
|
<td class="flex justify-center gap-4 border p-2">
|
|
<Icon
|
|
@click="editItem(item)"
|
|
name="i-lucide-pencil"
|
|
class="h-4 w-4 cursor-pointer"
|
|
/>
|
|
|
|
<Icon
|
|
@click="deleteItem(item.id)"
|
|
name="i-lucide-trash"
|
|
class="h-4 w-4 cursor-pointer"
|
|
/>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr v-if="list.length === 0">
|
|
<td
|
|
colspan="5"
|
|
class="p-4 text-center text-gray-500"
|
|
>
|
|
Belum ada data
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</template>
|