Squashed commit of the following:
commit 1a5cf194cd0d6eec85bbb8bf1c199df82aa79d4f Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Dec 2 10:30:51 2025 +0700 Squashed commit of the following: commit ec24dd8383e3a9c7cb7190b7ed9864ae7225805f Merge: 17cd42e7319cbcAuthor: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Tue Dec 2 10:18:33 2025 +0700 Merge branch 'dev' into feat/data-vaksin-192 commit 17cd42ef03d1cda15a8f85831e747723fbb2c0a5 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Dec 1 16:41:06 2025 +0700 Feat: UI Data Vaksin commit 1ced91229792420daca732256d535d100570d5bc Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Dec 1 14:10:27 2025 +0700 Squashed commit of the following: commit 8e6a6b3fd1a8ed6c19099b52f5d7fc38f6a1a39a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Fri Nov 28 14:32:47 2025 +0700 Feat: UI PRB commit 4f2da6cd1e077598fb7f3cdede8d771e9b39b2d7 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 27 19:41:18 2025 +0700 Squashed commit of the following: commit4a465f3992Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 24 13:40:22 2025 +0700 progress commit7811f051a5Merge:f060ed38aac6c4Author: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Mon Nov 24 10:25:15 2025 +0700 Merge branch 'dev' into feat/kfr-kemoterapi-174 commitf060ed33d2Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 24 10:21:20 2025 +0700 Feat: UI KFR commit399c3cbaeeAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 11:19:03 2025 +0700 Squashed commit of the following: commit 72ce2260c50597f782f07d29db3985607ecc2f34 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:19:36 2025 +0700 Feat: add doc preview in Therpay protocol List commit 7032cd2409a660d40899ffd421137e4158cfde4a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 15:34:30 2025 +0700 Fix: prepare API integration protokol terapi verification commit dbf6f78d00afc818baf2b34d128ee2153814cc88 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 14:09:28 2025 +0700 Feat: add basic CRUD therapy protocol commit 46a44e90fe4d4097b5460d2d4e5689b2a5389467 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 11 15:57:54 2025 +0700 Fix: Prepare protokol terapi API Integration commit 4674090566727cebd947e50e2c06c44e8c7afa7e Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:33:22 2025 +0700 Fix: hotfix style add protokol terapi commit 919c91abd8ef8b4cd193012eed7f5e8cf635cda2 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:17:14 2025 +0700 Fix: Typo drpodown-action-p in protokol-terapi commit e21d30eacf1a08118e289d4bb64889e708d5023a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:14:33 2025 +0700 Fix: add diagnose & procedure dialog picker in add protokol terapi commit 9a3d73b72b0dceea778d83e7630c5ead110a6a4c Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 10:21:24 2025 +0700 Fix: Add Schema therapy protocol rehab medik commit 4d8d2d633bbbd78038b1cc607558c1ceb31c5986 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 09:30:55 2025 +0700 Fix: refactor Actions Btn ba-dr-su commit 5f290a6e4bd1559c0e5864a508c5ab650cfae6e8 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 3 17:08:20 2025 +0700 Feat: UI protokol terapi in Rehab medik commit 63010f69ff30532bb8ac91443112f31d4942b221 Author: Khafid Prayoga <khafidp@pm.me> Date: Tue Oct 21 09:54:13 2025 +0700 wip: list protokol terapi commit 44eedc298680a5255fee0ee8feee3e24beda93dd Author: Khafid Prayoga <khafidp@pm.me> Date: Mon Oct 20 12:54:01 2025 +0700 feat(therapy-protocol): init form entry feat(therapy-protocol): init page routes wip: init entry form wip: form entry protokol terapi todo: table procedure, and diagnose picker wip: schema form new entry todo: picker/modal yang relateds ke form entry commit b2a6cdee0b7beb775830c4dceb69ff12c01d3ca4 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 26 14:44:57 2025 +0700 Squashed commit of the following: commit39b778ab78Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 14:39:41 2025 +0700 Feat: UI Laporan Operasi commitf6ae61849dAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 14:09:25 2025 +0700 Squashed commit of the following: commit 8e3ea9e8d1d7e3b06bc6e53e0b97f62222276171 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 16:14:03 2025 +0700 Feat: UI control letter history commit f11f97f936447bdb225918abb43313f8db540d67 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 15:18:25 2025 +0700 Squashed commit of the following: commitdab6adc4a9Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 11:19:48 2025 +0700 Fix: add role authorization in Resume commitc28fc8f7aaMerge:7ed1cc8bcfb4c1Author: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Tue Nov 18 09:02:16 2025 +0700 Merge branch 'dev' into feat/resume-81 commit7ed1cc83bfAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:04:01 2025 +0700 Feat: add doc preview in Resume List commitbcfb4c1456Merge:1cbde57975c87dAuthor: Munawwirul Jamal <57973347+munaja@users.noreply.github.com> Date: Mon Nov 17 11:15:14 2025 +0700 Merge pull request #147 from dikstub-rssa/feat/surat-kontrol-135 Feat: Integration Rehab Medik - Surat Kontrol commit15ab43c1b1Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 10:38:21 2025 +0700 Feat: add verification capthca and form adjustment commit53bd8e7f6eAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Fri Nov 7 08:55:23 2025 +0700 Fix: refactor rehab medik - Resume UI commitfc308809b8Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Oct 29 14:57:19 2025 +0700 Feat: add UI Rehab Medik > Proses > Resume commit9b383a5437Merge:a4dc7d7831749aAuthor: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Wed Oct 29 13:32:47 2025 +0700 Merge pull request #139 from dikstub-rssa/dev Update branch feat/resume-81 commit 2b7bea70d66e8472220a2a2406889fc489cc1ebd Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 11:20:51 2025 +0700 Fix: Typo in Control Letter commit 808e91527cf95de2a47387bb792a3af2e16d907b Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 10:59:50 2025 +0700 Fix: add role authorization in Control Letter commit1dd8e8e7b3Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 09:23:57 2025 +0700 Squashed commit of the following: commit 72ce2260c50597f782f07d29db3985607ecc2f34 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:19:36 2025 +0700 Feat: add doc preview in Therpay protocol List commit 7032cd2409a660d40899ffd421137e4158cfde4a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 15:34:30 2025 +0700 Fix: prepare API integration protokol terapi verification commit dbf6f78d00afc818baf2b34d128ee2153814cc88 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 14:09:28 2025 +0700 Feat: add basic CRUD therapy protocol commit 46a44e90fe4d4097b5460d2d4e5689b2a5389467 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 11 15:57:54 2025 +0700 Fix: Prepare protokol terapi API Integration commit 4674090566727cebd947e50e2c06c44e8c7afa7e Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:33:22 2025 +0700 Fix: hotfix style add protokol terapi commit 919c91abd8ef8b4cd193012eed7f5e8cf635cda2 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:17:14 2025 +0700 Fix: Typo drpodown-action-p in protokol-terapi commit e21d30eacf1a08118e289d4bb64889e708d5023a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:14:33 2025 +0700 Fix: add diagnose & procedure dialog picker in add protokol terapi commit 9a3d73b72b0dceea778d83e7630c5ead110a6a4c Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 10:21:24 2025 +0700 Fix: Add Schema therapy protocol rehab medik commit 4d8d2d633bbbd78038b1cc607558c1ceb31c5986 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 09:30:55 2025 +0700 Fix: refactor Actions Btn ba-dr-su commit 5f290a6e4bd1559c0e5864a508c5ab650cfae6e8 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 3 17:08:20 2025 +0700 Feat: UI protokol terapi in Rehab medik commit 63010f69ff30532bb8ac91443112f31d4942b221 Author: Khafid Prayoga <khafidp@pm.me> Date: Tue Oct 21 09:54:13 2025 +0700 wip: list protokol terapi commit 44eedc298680a5255fee0ee8feee3e24beda93dd Author: Khafid Prayoga <khafidp@pm.me> Date: Mon Oct 20 12:54:01 2025 +0700 feat(therapy-protocol): init form entry feat(therapy-protocol): init page routes wip: init entry form wip: form entry protokol terapi todo: table procedure, and diagnose picker wip: schema form new entry todo: picker/modal yang relateds ke form entry commit3e5c03148bAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 09:17:11 2025 +0700 progress commit a89c480474c025fb683383693e6a6808baa1d0d6 Merge: 8e6a6b37319cbcAuthor: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Tue Dec 2 10:27:07 2025 +0700 Merge branch 'dev' into feat/prb-189 commit 8e6a6b3fd1a8ed6c19099b52f5d7fc38f6a1a39a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Fri Nov 28 14:32:47 2025 +0700 Feat: UI PRB commit 4f2da6cd1e077598fb7f3cdede8d771e9b39b2d7 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 27 19:41:18 2025 +0700 Squashed commit of the following: commit4a465f3992Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 24 13:40:22 2025 +0700 progress commit7811f051a5Merge:f060ed38aac6c4Author: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Mon Nov 24 10:25:15 2025 +0700 Merge branch 'dev' into feat/kfr-kemoterapi-174 commitf060ed33d2Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 24 10:21:20 2025 +0700 Feat: UI KFR commit399c3cbaeeAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 11:19:03 2025 +0700 Squashed commit of the following: commit 72ce2260c50597f782f07d29db3985607ecc2f34 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:19:36 2025 +0700 Feat: add doc preview in Therpay protocol List commit 7032cd2409a660d40899ffd421137e4158cfde4a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 15:34:30 2025 +0700 Fix: prepare API integration protokol terapi verification commit dbf6f78d00afc818baf2b34d128ee2153814cc88 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 14:09:28 2025 +0700 Feat: add basic CRUD therapy protocol commit 46a44e90fe4d4097b5460d2d4e5689b2a5389467 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 11 15:57:54 2025 +0700 Fix: Prepare protokol terapi API Integration commit 4674090566727cebd947e50e2c06c44e8c7afa7e Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:33:22 2025 +0700 Fix: hotfix style add protokol terapi commit 919c91abd8ef8b4cd193012eed7f5e8cf635cda2 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:17:14 2025 +0700 Fix: Typo drpodown-action-p in protokol-terapi commit e21d30eacf1a08118e289d4bb64889e708d5023a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:14:33 2025 +0700 Fix: add diagnose & procedure dialog picker in add protokol terapi commit 9a3d73b72b0dceea778d83e7630c5ead110a6a4c Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 10:21:24 2025 +0700 Fix: Add Schema therapy protocol rehab medik commit 4d8d2d633bbbd78038b1cc607558c1ceb31c5986 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 09:30:55 2025 +0700 Fix: refactor Actions Btn ba-dr-su commit 5f290a6e4bd1559c0e5864a508c5ab650cfae6e8 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 3 17:08:20 2025 +0700 Feat: UI protokol terapi in Rehab medik commit 63010f69ff30532bb8ac91443112f31d4942b221 Author: Khafid Prayoga <khafidp@pm.me> Date: Tue Oct 21 09:54:13 2025 +0700 wip: list protokol terapi commit 44eedc298680a5255fee0ee8feee3e24beda93dd Author: Khafid Prayoga <khafidp@pm.me> Date: Mon Oct 20 12:54:01 2025 +0700 feat(therapy-protocol): init form entry feat(therapy-protocol): init page routes wip: init entry form wip: form entry protokol terapi todo: table procedure, and diagnose picker wip: schema form new entry todo: picker/modal yang relateds ke form entry commit b2a6cdee0b7beb775830c4dceb69ff12c01d3ca4 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 26 14:44:57 2025 +0700 Squashed commit of the following: commit39b778ab78Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 14:39:41 2025 +0700 Feat: UI Laporan Operasi commitf6ae61849dAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 14:09:25 2025 +0700 Squashed commit of the following: commit 8e3ea9e8d1d7e3b06bc6e53e0b97f62222276171 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 16:14:03 2025 +0700 Feat: UI control letter history commit f11f97f936447bdb225918abb43313f8db540d67 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Thu Nov 20 15:18:25 2025 +0700 Squashed commit of the following: commitdab6adc4a9Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 11:19:48 2025 +0700 Fix: add role authorization in Resume commitc28fc8f7aaMerge:7ed1cc8bcfb4c1Author: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Tue Nov 18 09:02:16 2025 +0700 Merge branch 'dev' into feat/resume-81 commit7ed1cc83bfAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:04:01 2025 +0700 Feat: add doc preview in Resume List commitbcfb4c1456Merge:1cbde57975c87dAuthor: Munawwirul Jamal <57973347+munaja@users.noreply.github.com> Date: Mon Nov 17 11:15:14 2025 +0700 Merge pull request #147 from dikstub-rssa/feat/surat-kontrol-135 Feat: Integration Rehab Medik - Surat Kontrol commit15ab43c1b1Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 10:38:21 2025 +0700 Feat: add verification capthca and form adjustment commit53bd8e7f6eAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Fri Nov 7 08:55:23 2025 +0700 Fix: refactor rehab medik - Resume UI commitfc308809b8Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Oct 29 14:57:19 2025 +0700 Feat: add UI Rehab Medik > Proses > Resume commit9b383a5437Merge:a4dc7d7831749aAuthor: Muhammad Hasyim Chaidir Ali <68959522+Hasyim-Kai@users.noreply.github.com> Date: Wed Oct 29 13:32:47 2025 +0700 Merge pull request #139 from dikstub-rssa/dev Update branch feat/resume-81 commit 2b7bea70d66e8472220a2a2406889fc489cc1ebd Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 11:20:51 2025 +0700 Fix: Typo in Control Letter commit 808e91527cf95de2a47387bb792a3af2e16d907b Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 18 10:59:50 2025 +0700 Fix: add role authorization in Control Letter commit1dd8e8e7b3Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 09:23:57 2025 +0700 Squashed commit of the following: commit 72ce2260c50597f782f07d29db3985607ecc2f34 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 17 15:19:36 2025 +0700 Feat: add doc preview in Therpay protocol List commit 7032cd2409a660d40899ffd421137e4158cfde4a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 15:34:30 2025 +0700 Fix: prepare API integration protokol terapi verification commit dbf6f78d00afc818baf2b34d128ee2153814cc88 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Wed Nov 12 14:09:28 2025 +0700 Feat: add basic CRUD therapy protocol commit 46a44e90fe4d4097b5460d2d4e5689b2a5389467 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 11 15:57:54 2025 +0700 Fix: Prepare protokol terapi API Integration commit 4674090566727cebd947e50e2c06c44e8c7afa7e Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:33:22 2025 +0700 Fix: hotfix style add protokol terapi commit 919c91abd8ef8b4cd193012eed7f5e8cf635cda2 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:17:14 2025 +0700 Fix: Typo drpodown-action-p in protokol-terapi commit e21d30eacf1a08118e289d4bb64889e708d5023a Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 10 15:14:33 2025 +0700 Fix: add diagnose & procedure dialog picker in add protokol terapi commit 9a3d73b72b0dceea778d83e7630c5ead110a6a4c Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 10:21:24 2025 +0700 Fix: Add Schema therapy protocol rehab medik commit 4d8d2d633bbbd78038b1cc607558c1ceb31c5986 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 4 09:30:55 2025 +0700 Fix: refactor Actions Btn ba-dr-su commit 5f290a6e4bd1559c0e5864a508c5ab650cfae6e8 Author: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Mon Nov 3 17:08:20 2025 +0700 Feat: UI protokol terapi in Rehab medik commit 63010f69ff30532bb8ac91443112f31d4942b221 Author: Khafid Prayoga <khafidp@pm.me> Date: Tue Oct 21 09:54:13 2025 +0700 wip: list protokol terapi commit 44eedc298680a5255fee0ee8feee3e24beda93dd Author: Khafid Prayoga <khafidp@pm.me> Date: Mon Oct 20 12:54:01 2025 +0700 feat(therapy-protocol): init form entry feat(therapy-protocol): init page routes wip: init entry form wip: form entry protokol terapi todo: table procedure, and diagnose picker wip: schema form new entry todo: picker/modal yang relateds ke form entry commit3e5c03148bAuthor: hasyim_kai <muhammad.hasyim.c.a@gmail.com> Date: Tue Nov 25 09:17:11 2025 +0700 progress
This commit is contained in:
@@ -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,73 @@
|
||||
<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 { cn } from '~/lib/utils'
|
||||
|
||||
interface InstallationFormData {
|
||||
name: string
|
||||
code: string
|
||||
encounterClassCode: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
schema: any
|
||||
initialValues?: Partial<InstallationFormData>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [values: InstallationFormData, resetForm: () => void]
|
||||
reset: [resetForm: () => void]
|
||||
}>()
|
||||
|
||||
// Form submission handler
|
||||
function onSubmitForm(values: any, { resetForm }: { resetForm: () => void }) {
|
||||
emit('submit', values, resetForm)
|
||||
}
|
||||
|
||||
// Form cancel handler
|
||||
function onResetForm({ resetForm }: { resetForm: () => void }) {
|
||||
emit('reset', resetForm)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
v-slot="{ handleSubmit, resetForm }"
|
||||
as=""
|
||||
keep-values
|
||||
:initial-values="initialValues"
|
||||
>
|
||||
<form id="entry-form" @submit="handleSubmit($event, (values) => onSubmitForm(values, { resetForm }))">
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-7 text-lg xl:text-xl">
|
||||
<div class="flex flex-col justify-between">
|
||||
<InputBase
|
||||
field-name="patientName"
|
||||
label="Nama Pasien"
|
||||
placeholder="Nama Pasien"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="cardNumber"
|
||||
label="Nomor Kartu"
|
||||
placeholder="Nomor Kartu"
|
||||
/>
|
||||
<InputBase
|
||||
field-name="sepNumber"
|
||||
label="Nomor SEP"
|
||||
placeholder="Nomor SEP"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-2 flex items-center gap-3 justify-end">
|
||||
<Button @click="onResetForm" variant="secondary">Reset</Button>
|
||||
<Button @click="onSubmitForm">Terapkan</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
isDisabled?: boolean
|
||||
isWithTime?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'birthDate',
|
||||
label = 'Tanggal Lahir',
|
||||
placeholder = 'Pilih tanggal lahir',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
// Reactive variables for age calculation
|
||||
const patientAge = ref<string>('Masukkan tanggal lahir')
|
||||
|
||||
// Function to calculate age with years, months, and days
|
||||
function calculateAge(birthDate: string | Date | undefined): string {
|
||||
if (!birthDate) {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
|
||||
try {
|
||||
let dateObj: Date
|
||||
|
||||
if (typeof birthDate === 'string') {
|
||||
dateObj = parseISO(birthDate)
|
||||
} else {
|
||||
dateObj = birthDate
|
||||
}
|
||||
|
||||
const today = new Date()
|
||||
|
||||
// Calculate years, months, and days
|
||||
const totalYears = differenceInYears(today, dateObj)
|
||||
|
||||
// Calculate remaining months after years
|
||||
const yearsPassed = new Date(dateObj)
|
||||
yearsPassed.setFullYear(yearsPassed.getFullYear() + totalYears)
|
||||
const remainingMonths = differenceInMonths(today, yearsPassed)
|
||||
|
||||
// Calculate remaining days after years and months
|
||||
const monthsPassed = new Date(yearsPassed)
|
||||
monthsPassed.setMonth(monthsPassed.getMonth() + remainingMonths)
|
||||
const remainingDays = differenceInDays(today, monthsPassed)
|
||||
|
||||
// Format the result
|
||||
const parts = []
|
||||
if (totalYears > 0) parts.push(`${totalYears} Tahun`)
|
||||
if (remainingMonths > 0) parts.push(`${remainingMonths} Bulan`)
|
||||
if (remainingDays > 0) parts.push(`${remainingDays} Hari`)
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : '0 Hari'
|
||||
} catch {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
}
|
||||
</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 && !isDisabled"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="birthDate"
|
||||
:type="props.isWithTime ? 'datetime-local' : 'date'"
|
||||
min="1900-01-01"
|
||||
v-bind="componentField"
|
||||
:placeholder="placeholder"
|
||||
:disabled="isDisabled"
|
||||
@update:model-value="
|
||||
(value: string | number) => {
|
||||
const dateStr = typeof value === 'number' ? String(value) : value
|
||||
patientAge = calculateAge(dateStr)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const doctors = ref<Array<Item>>([])
|
||||
|
||||
async function fetchDpjp() {
|
||||
doctors.value = await getDoctorLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
includes: 'employee-person',
|
||||
}, true)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDpjp()
|
||||
})
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
</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"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="doctors"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const doctors = ref<Array<Item>>([])
|
||||
|
||||
async function fetchDpjp() {
|
||||
doctors.value = await getDoctorLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
includes: 'employee-person',
|
||||
}, true)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDpjp()
|
||||
})
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
</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"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="doctors"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
const doctors = ref<Array<Item>>([])
|
||||
|
||||
async function fetchDpjp() {
|
||||
doctors.value = await getDoctorLabelList({
|
||||
serviceType: 1,
|
||||
serviceDate: new Date().toISOString().substring(0, 10),
|
||||
includes: 'employee-person',
|
||||
}, true)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDpjp()
|
||||
})
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
</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"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="doctors"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { PrbProgramTypeOptList } from '~/lib/constants'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
</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"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="PrbProgramTypeOptList"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</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'
|
||||
import { educationCodes, genderCodes } from '~/lib/constants'
|
||||
import { calculateAge } from '~/lib/utils'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-upd.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {width: 50}, ],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'No. PRB' },
|
||||
{ label: 'No. RM' },
|
||||
{ label: 'Nama Pasien' },
|
||||
{ label: 'Jenis Kelamin' },
|
||||
{ label: 'Alamat' },
|
||||
{ label: 'Klinik' },
|
||||
{ label: 'Nama Dokter' },
|
||||
{ label: 'Tanggal' },
|
||||
{ label: 'Program PRB' },
|
||||
{ label: 'No. SEP' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'name1', 'name2', 'name3', 'name4', 'name5', 'name6', 'name7', 'name8', 'name9', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
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: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
||||
import { type Variants, Badge } from '~/components/pub/ui/badge'
|
||||
import { cn, } from '~/lib/utils'
|
||||
import type { Prb } from '~/models/prb'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
instance: Prb | null
|
||||
}>()
|
||||
|
||||
// const emit = defineEmits<{
|
||||
// (e: 'click'): void
|
||||
// }>()
|
||||
|
||||
const dummy = [
|
||||
{
|
||||
id: 1,
|
||||
number: 1,
|
||||
name: 'Operasi',
|
||||
code: 'OP-001'
|
||||
}
|
||||
]
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
// #endregion
|
||||
|
||||
// Computed addresses from nested data
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
// function onClick() {
|
||||
// emit('click')
|
||||
// }
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article :class="cn('mb-5 space-y-1',)">
|
||||
<DetailRow label="No. PRB">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Waktu dan Tanggal">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Program PRB">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="DPJP">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Keterangan">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Saran">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import SelectDate from './_common/select-date.vue'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import ObatPicker from './obat-picker/picker-dialog.vue'
|
||||
import SepPicker from './sep-picker/picker-dialog.vue'
|
||||
import SelectDpjp from './_common/select-dpjp.vue'
|
||||
import SelectProgram from './_common/select-program.vue'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
schema: any
|
||||
initialValues?: any
|
||||
errors?: FormErrors
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
const isSepDialogOpen = ref(false)
|
||||
provide("isSepDialogOpen", isSepDialogOpen);
|
||||
const handleToggleSepDialog = () => isSepDialogOpen.value = !isSepDialogOpen.value
|
||||
const setNoSep = (inputValue: string) => formRef.value?.setValues({ a0: inputValue }, false)
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ? initialValues : {}"
|
||||
>
|
||||
<DE.Block :col-count="2" :cell-flex="false">
|
||||
<DE.Cell :col-span="2" v-if="isBpjs">
|
||||
<div class="w-1/2 flex items-end gap-3">
|
||||
<InputBase :errors="errors"
|
||||
field-name="a0"
|
||||
label="No. RM/Kartu BPJS" placeholder="Masukkan No. RM/Kartu BPJS"
|
||||
/>
|
||||
<Button @click="handleToggleSepDialog" size="icon" variant="outline"
|
||||
class="text-orange-400 border-orange-400 bg-transparent">
|
||||
<Icon name="i-lucide-search" class="h-4 w-4 align-middle transition-colors" />
|
||||
</Button>
|
||||
</div>
|
||||
</DE.Cell>
|
||||
<InputBase :errors="errors"
|
||||
field-name="a1"
|
||||
label="No. PRB" placeholder="Akan terisi otomatis"
|
||||
is-disabled
|
||||
/>
|
||||
<SelectDate
|
||||
field-name="a2"
|
||||
label="Waktu & Tanggal"
|
||||
:errors="errors"
|
||||
is-disabled
|
||||
is-with-time
|
||||
/>
|
||||
<SelectProgram :errors="errors"
|
||||
field-name="a3"
|
||||
label="Program PRB" placeholder="Pilih Program PRB"
|
||||
is-required
|
||||
/>
|
||||
<SelectDpjp :errors="errors"
|
||||
field-name="a4"
|
||||
label="DPJP" placeholder="Pilih DPJP"
|
||||
is-required
|
||||
/>
|
||||
<TextAreaInput :errors="errors"
|
||||
field-name="a5"
|
||||
label="Keterangan" placeholder="Isi Keterangan"
|
||||
/>
|
||||
<TextAreaInput :errors="errors"
|
||||
field-name="a6"
|
||||
label="Saran" placeholder="Isi Saran"
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<!-- PICKER -->
|
||||
<DE.Block :col-count="1" :cell-flex="false">
|
||||
<ObatPicker field-name="a7" title="Obat" />
|
||||
</DE.Block>
|
||||
<SepPicker title="SEP" :choose-fn="setNoSep" />
|
||||
<!-- -->
|
||||
</Form>
|
||||
</template>
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-print.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('~/components/pub/my-ui/badge/status-badge.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {}, {}, {width: 100}, {width: 50}, ],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Tgl Diterbitkan' },
|
||||
{ label: 'Kode Obat' },
|
||||
{ label: 'Nama Obat' },
|
||||
{ label: 'Signa' },
|
||||
{ label: 'Jumlah' },
|
||||
{ label: 'Durasi Pemberian' },
|
||||
{ label: 'status' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'name1', 'name2', 'name3', 'name4', 'name5', 'status', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
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: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
status(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<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
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
'update:dateValue': [value: DateRange]
|
||||
}>()
|
||||
|
||||
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,56 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { educationCodes, genderCodes } from '~/lib/constants'
|
||||
import { calculateAge } from '~/lib/utils'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-upd.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {}, {}, {width: 50}, ],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Tgl Diterbitkan' },
|
||||
{ label: 'Kode Obat' },
|
||||
{ label: 'Nama Obat' },
|
||||
{ label: 'Signa' },
|
||||
{ label: 'Jumlah' },
|
||||
{ label: 'Durasi Pemberian' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['date', 'name1', 'name2', 'name3', 'name4', 'name5', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
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: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<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 './list.cfg'
|
||||
import { config as bpjsConfig } from './bpjs-list.cfg'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
data: any[]
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
|
||||
const tableConfig = computed(() => {
|
||||
return props.isBpjs ? bpjsConfig : config
|
||||
})
|
||||
// const emit = defineEmits<{
|
||||
// pageChange: [page: number]
|
||||
// }>()
|
||||
|
||||
// function handlePageChange(page: number) {
|
||||
// emit('pageChange', page)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="tableConfig"
|
||||
:rows="data"
|
||||
/>
|
||||
<!-- <PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" /> -->
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,114 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
// Helpers
|
||||
|
||||
// Types
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
||||
// Handlers
|
||||
import {
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} from '~/handlers/procedure-src.handler'
|
||||
|
||||
// Services
|
||||
import { getList, getDetail } from '~/services/procedure-src.service'
|
||||
import SelectMedicine from '../_common/select-medicine.vue'
|
||||
import SelectMedicineForm from '../_common/select-medicine-form.vue'
|
||||
|
||||
interface Props {
|
||||
schema: any
|
||||
initialValues?: any
|
||||
errors?: FormErrors
|
||||
medicineData: any[]
|
||||
medicineFormsData: any[]
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleDialog: []
|
||||
}>()
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ? initialValues : {}"
|
||||
>
|
||||
<DE.Block :col-count="6" :cell-flex="false">
|
||||
<DE.Cell :col-span="6">
|
||||
<SelectMedicine :errors="errors"
|
||||
field-name="a1"
|
||||
label="Obat" placeholder="Pilih Obat"
|
||||
/>
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="4" class="mt-1.5">
|
||||
<h1 class="mb-1">Signa</h1>
|
||||
<div class="flex items-start gap-3">
|
||||
<InputBase :errors="errors"
|
||||
field-name="a2"
|
||||
label="" placeholder="0"
|
||||
right-label="Frekuensi"
|
||||
numeric-only
|
||||
/>
|
||||
<p class="text-3xl text-gray-400">X</p>
|
||||
<InputBase :errors="errors"
|
||||
field-name="a3"
|
||||
label="" placeholder="0"
|
||||
right-label="Dosis"
|
||||
numeric-only
|
||||
/>
|
||||
</div>
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="2">
|
||||
<SelectMedicineForm :errors="errors"
|
||||
field-name="a4"
|
||||
label="Satuan" placeholder="Pilih Satuan"
|
||||
/>
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="3">
|
||||
<InputBase :errors="errors"
|
||||
field-name="a5"
|
||||
label="Jumlah" placeholder="Masukkan Jumlah"
|
||||
/>
|
||||
</DE.Cell>
|
||||
<DE.Cell :col-span="3">
|
||||
<InputBase :errors="errors"
|
||||
field-name="a6"
|
||||
label="Durasi Pemberian" placeholder="Masukkan Durasi Pemberian"
|
||||
right-label="Hari"
|
||||
numeric-only
|
||||
/>
|
||||
</DE.Cell>
|
||||
</DE.Block>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -0,0 +1,140 @@
|
||||
<script setup lang="ts">
|
||||
import ProcedureListDialog from './form.vue'
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { FieldArray } from 'vee-validate'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import TextAreaInput from '~/components/pub/my-ui/form/text-area-input.vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import TableHeader from '~/components/pub/ui/table/TableHeader.vue'
|
||||
import { is } from 'date-fns/locale'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import List from './form.vue'
|
||||
import { getValueLabelList as getMedicineFormList } from '~/services/medicine-form.service'
|
||||
import { getValueLabelList as getMedicineList } from '~/services/medicine.service'
|
||||
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import FormEntry from './form.vue'
|
||||
import { PrbSchema } from '~/schemas/prb.schema'
|
||||
import type { ExposedForm } from '~/types/form'
|
||||
import type { Prb } from '~/models/prb'
|
||||
import { PrbMedicineSchema } from '~/schemas/prb-medicine.schema'
|
||||
|
||||
interface Props {
|
||||
fieldName: string
|
||||
title: string
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const isOperativeActionDialogOpen = ref(false)
|
||||
provide("isOperativeActionDialogOpen", isOperativeActionDialogOpen);
|
||||
|
||||
const inputForm = ref<ExposedForm<any> | null>(null)
|
||||
const medicine = ref<{ value: string; label: string }[]>([])
|
||||
const medicineForms = ref<{ value: string; label: string }[]>([])
|
||||
|
||||
const handleToggleOperativeActionDialog = () => {
|
||||
isOperativeActionDialogOpen.value = !isOperativeActionDialogOpen.value
|
||||
}
|
||||
|
||||
async function init(){
|
||||
const [medicineRes, medicineFormRes] = await Promise.all([
|
||||
getMedicineList({ sort: 'createdAt:asc', }),
|
||||
getMedicineFormList({ sort: 'createdAt:asc', })
|
||||
])
|
||||
medicine.value = medicineRes
|
||||
medicineForms.value = medicineFormRes
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
init()
|
||||
})
|
||||
|
||||
async function handleAdd(psuhFn: (input: unknown) => void) {
|
||||
const validated = await composeFormData()
|
||||
psuhFn({
|
||||
code: validated.a1,
|
||||
name: validated.a2,
|
||||
signa: validated.a3,
|
||||
total: validated.a5,
|
||||
duration: validated.a6,
|
||||
})
|
||||
handleToggleOperativeActionDialog()
|
||||
}
|
||||
|
||||
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
|
||||
return new Promise((resolve) => resolve(formData))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="mb-2 flex items-center gap-3">
|
||||
<h1 class="font-medium text-base">{{ title }}</h1>
|
||||
<Button @click="isOperativeActionDialogOpen = true" size="xs" variant="outline"
|
||||
class="rounded-lg text-orange-400 border-orange-400 bg-transparent">
|
||||
<Icon name="i-lucide-search" class="h-4 w-4 align-middle transition-colors" />
|
||||
Pilih {{ title }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<FieldArray v-slot="{ fields, push, remove }" :name="props.fieldName">
|
||||
<Dialog
|
||||
v-model:open="isOperativeActionDialogOpen"
|
||||
title="" size="xl">
|
||||
<FormEntry
|
||||
ref="inputForm"
|
||||
:schema="PrbMedicineSchema"
|
||||
:medicine-data="medicine"
|
||||
:medicine-forms-data="medicineForms"
|
||||
@toggle-dialog="handleToggleOperativeActionDialog"
|
||||
/>
|
||||
<div class="mt-2 flex justify-end">
|
||||
<Button @click="handleAdd(push)" class="">
|
||||
<Icon name="i-lucide-save" class="h-4 w-4 align-middle transition-colors" />
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<div class="border border-gray-200 rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader class="bg-gray-100">
|
||||
<TableRow>
|
||||
<TableHead class="">Code</TableHead>
|
||||
<TableHead class="">Name</TableHead>
|
||||
<TableHead class="">Signa</TableHead>
|
||||
<TableHead class="">Jumlah</TableHead>
|
||||
<TableHead class="">Durasi Pemberian</TableHead>
|
||||
<TableHead class="w-24">Action</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow v-for="(field, idx) in fields" :key="idx">
|
||||
<TableCell class="2xl:pl-4">{{ field.value?.code }}</TableCell>
|
||||
<TableCell class="2xl:pl-4">{{ field.value?.name }}</TableCell>
|
||||
<TableCell class="2xl:pl-4">{{ field.value?.signa }}</TableCell>
|
||||
<TableCell class="2xl:pl-4">{{ field.value?.total }}</TableCell>
|
||||
<TableCell class="2xl:pl-4">{{ field.value?.duration }}</TableCell>
|
||||
<TableCell class="2xl:pl-4">
|
||||
<Button type="button" variant="destructive" size="sm" @click="remove(idx)">
|
||||
<Icon name="i-lucide-trash-2" class="h-4 w-4" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</FieldArray>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-choose.vue'))
|
||||
const statusBadge = defineAsyncComponent(() => import('~/components/pub/my-ui/badge/status-badge.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [
|
||||
{ width: 120 }, // TGL. SEP
|
||||
{ width: 150 }, // NO. SEP
|
||||
{ width: 120 }, // PELAYANAN
|
||||
{ width: 100 }, // JALUR
|
||||
{ width: 150 }, // NO. RM
|
||||
{ width: 200 }, // NAMA PASIEN
|
||||
{ width: 150 }, // NO. KARTU BPJS
|
||||
{ width: 150 }, // NO. SURAT KONTROL
|
||||
{ width: 150 }, // TGL SURAT KONTROL
|
||||
{ width: 150 }, // KLINIK TUJUAN
|
||||
{ width: 200 }, // DPJP
|
||||
{ width: 200 }, // DIAGNOSIS AWAL
|
||||
{ width: 100 }, // AKSI
|
||||
],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'TGL. SEP' },
|
||||
{ label: 'NO. SEP' },
|
||||
{ label: 'PELAYANAN' },
|
||||
{ label: 'JALUR' },
|
||||
{ label: 'NO. RM' },
|
||||
{ label: 'NAMA PASIEN' },
|
||||
{ label: 'NO. KARTU BPJS' },
|
||||
{ label: 'NO. SURAT KONTROL' },
|
||||
{ label: 'TGL SURAT KONTROL' },
|
||||
{ label: 'KLINIK TUJUAN' },
|
||||
{ label: 'DPJP' },
|
||||
{ label: 'DIAGNOSIS AWAL' },
|
||||
{ label: 'AKSI' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: [
|
||||
'letterDate',
|
||||
'letterNumber',
|
||||
'serviceType',
|
||||
'flow',
|
||||
'medicalRecordNumber',
|
||||
'patientName',
|
||||
'cardNumber',
|
||||
'controlLetterNumber',
|
||||
'controlLetterDate',
|
||||
'clinicDestination',
|
||||
'attendingDoctor',
|
||||
'diagnosis',
|
||||
'action',
|
||||
],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'letterNumber', label: 'NO. SEP' },
|
||||
{ key: 'patientName', label: 'Nama Pasien' },
|
||||
],
|
||||
|
||||
parses: {},
|
||||
|
||||
components: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
status(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: statusBadge,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {},
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
// Components
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
|
||||
|
||||
// Helpers
|
||||
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import { config } from './list.cfg'
|
||||
|
||||
// Types
|
||||
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||
import { ProcedureSrcSchema, type ProcedureSrcFormData } from '~/schemas/procedure-src.schema'
|
||||
|
||||
// Handlers
|
||||
import {
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} from '~/handlers/procedure-src.handler'
|
||||
|
||||
// Services
|
||||
import { getList, getDetail } from '~/services/procedure-src.service'
|
||||
|
||||
const title = ref('')
|
||||
|
||||
interface Props {
|
||||
data: any[]
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleDialog: []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="config"
|
||||
:rows="data"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,141 @@
|
||||
<script setup lang="ts">
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import List from './list.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { ActionEvents, type HeaderPrep, type RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
import { getList } from '~/services/vclaim-monitoring-visit.service'
|
||||
import type { VclaimSepData } from '~/models/vclaim'
|
||||
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
|
||||
import { defaultQuerySchema, defaultQueryParams } from '~/composables/usePaginatedList'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
chooseFn: (inputValue: string) => void
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const isSepDialogOpen = inject('isSepDialogOpen') as Ref<boolean>;
|
||||
const handleToggleSepDialog = () => isSepDialogOpen.value = !isSepDialogOpen.value
|
||||
|
||||
const data = ref<VclaimSepData[]>([])// Search model with debounce
|
||||
const searchInput = ref('')
|
||||
const debouncedSearch = refDebounced(searchInput, 500) // 500ms debounce
|
||||
// const paginationMeta = reactive<PaginationMeta>({
|
||||
// recordCount: 0,
|
||||
// page: 1,
|
||||
// pageSize: 10,
|
||||
// totalPage: 5,
|
||||
// hasNext: false,
|
||||
// hasPrev: false,
|
||||
// })
|
||||
// // URL state management
|
||||
// const queryParams = useUrlSearchParams('history', {
|
||||
// initialValue: defaultQueryParams,
|
||||
// removeFalsyValues: true,
|
||||
// })
|
||||
// const params = computed(() => {
|
||||
// const result = defaultQuerySchema.safeParse(queryParams)
|
||||
// return result.data || defaultQueryParams
|
||||
// })
|
||||
|
||||
const recId = ref<string>(``)
|
||||
const recAction = ref<string>(``)
|
||||
const recItem = ref<any>({})
|
||||
const timestamp = ref<any>({})
|
||||
provide('rec_id', recId)
|
||||
provide('rec_action', recAction)
|
||||
provide('rec_item', recItem)
|
||||
provide('timestamp', timestamp)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: props.title,
|
||||
icon: 'i-lucide-clipboard-list',
|
||||
}
|
||||
const refSearchNav: RefSearchNav = {
|
||||
onClick: () => {
|
||||
// open filter modal
|
||||
},
|
||||
onInput: (val: string) => {
|
||||
searchInput.value = val
|
||||
},
|
||||
onClear: () => {
|
||||
searchInput.value = ''
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async function getMonitoringVisitMappers() {
|
||||
data.value = []
|
||||
const tempArr: VclaimSepData[] = []
|
||||
|
||||
const result = await getList({
|
||||
serviceType: 1,
|
||||
search: debouncedSearch.value,
|
||||
})
|
||||
|
||||
if (result && result.success && result.body) {
|
||||
const visitsRaw = result.body?.response?.sep || []
|
||||
|
||||
visitsRaw.forEach((result: any) => {
|
||||
// Format pelayanan: "R.Inap" -> "Rawat Inap", "1" -> "Rawat Jalan", dll
|
||||
let serviceType = result.jnsPelayanan || '-'
|
||||
if (serviceType === 'R.Inap') {
|
||||
serviceType = 'Rawat Inap'
|
||||
} else if (serviceType === '1' || serviceType === 'R.Jalan') {
|
||||
serviceType = 'Rawat Jalan'
|
||||
}
|
||||
|
||||
tempArr.push({
|
||||
letterDate: result.tglSep || '-',
|
||||
letterNumber: result.noSep || '-',
|
||||
serviceType: serviceType,
|
||||
flow: '-',
|
||||
medicalRecordNumber: '-',
|
||||
patientName: result.nama || '-',
|
||||
cardNumber: result.noKartu || '-',
|
||||
controlLetterNumber: result.noRujukan || '-',
|
||||
controlLetterDate: result.tglPlgSep || '-',
|
||||
clinicDestination: result.poli || '-',
|
||||
attendingDoctor: '-',
|
||||
diagnosis: result.diagnosa || '-',
|
||||
careClass: result.kelasRawat || '-',
|
||||
})
|
||||
})
|
||||
data.value = tempArr
|
||||
}
|
||||
}
|
||||
// Handle pagination page change
|
||||
// function handlePageChange(page: number) {
|
||||
// // Update URL params - this will trigger watcher
|
||||
// queryParams['page-number'] = page
|
||||
// }
|
||||
|
||||
onMounted(async () => {
|
||||
await getMonitoringVisitMappers()
|
||||
})
|
||||
|
||||
|
||||
watch([recId, recAction, timestamp], () => {
|
||||
switch (recAction.value) {
|
||||
case ActionEvents.showProcess:
|
||||
props.chooseFn(recItem.value.cardNumber)
|
||||
handleToggleSepDialog()
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
watch(debouncedSearch, () => {
|
||||
getMonitoringVisitMappers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<Dialog v-model:open="isSepDialogOpen" title="" size="full">
|
||||
<Header
|
||||
v-model="searchInput"
|
||||
:prep="headerPrep"
|
||||
:ref-search-nav="refSearchNav" />
|
||||
<List :data="data" />
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { differenceInDays, differenceInMonths, differenceInYears, parseISO } from 'date-fns'
|
||||
import { Input } from '~/components/pub/ui/input'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
isDisabled?: boolean
|
||||
isWithTime?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'birthDate',
|
||||
label = 'Tanggal Lahir',
|
||||
placeholder = 'Pilih tanggal lahir',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
// Reactive variables for age calculation
|
||||
const patientAge = ref<string>('Masukkan tanggal lahir')
|
||||
|
||||
// Function to calculate age with years, months, and days
|
||||
function calculateAge(birthDate: string | Date | undefined): string {
|
||||
if (!birthDate) {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
|
||||
try {
|
||||
let dateObj: Date
|
||||
|
||||
if (typeof birthDate === 'string') {
|
||||
dateObj = parseISO(birthDate)
|
||||
} else {
|
||||
dateObj = birthDate
|
||||
}
|
||||
|
||||
const today = new Date()
|
||||
|
||||
// Calculate years, months, and days
|
||||
const totalYears = differenceInYears(today, dateObj)
|
||||
|
||||
// Calculate remaining months after years
|
||||
const yearsPassed = new Date(dateObj)
|
||||
yearsPassed.setFullYear(yearsPassed.getFullYear() + totalYears)
|
||||
const remainingMonths = differenceInMonths(today, yearsPassed)
|
||||
|
||||
// Calculate remaining days after years and months
|
||||
const monthsPassed = new Date(yearsPassed)
|
||||
monthsPassed.setMonth(monthsPassed.getMonth() + remainingMonths)
|
||||
const remainingDays = differenceInDays(today, monthsPassed)
|
||||
|
||||
// Format the result
|
||||
const parts = []
|
||||
if (totalYears > 0) parts.push(`${totalYears} Tahun`)
|
||||
if (remainingMonths > 0) parts.push(`${remainingMonths} Bulan`)
|
||||
if (remainingDays > 0) parts.push(`${remainingDays} Hari`)
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : '0 Hari'
|
||||
} catch {
|
||||
return 'Masukkan tanggal lahir'
|
||||
}
|
||||
}
|
||||
</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 && !isDisabled"
|
||||
>
|
||||
{{ label }}
|
||||
</DE.Label>
|
||||
<DE.Field
|
||||
:id="fieldName"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
id="birthDate"
|
||||
:type="props.isWithTime ? 'datetime-local' : 'date'"
|
||||
min="1900-01-01"
|
||||
v-bind="componentField"
|
||||
:placeholder="placeholder"
|
||||
:disabled="isDisabled"
|
||||
@update:model-value="
|
||||
(value: string | number) => {
|
||||
const dateStr = typeof value === 'number' ? String(value) : value
|
||||
patientAge = calculateAge(dateStr)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import { cn, mapToComboboxOptList } from '~/lib/utils'
|
||||
import { PrbProgramTypeOptList } from '~/lib/constants'
|
||||
import { getValueLabelList as getDoctorLabelList } from '~/services/doctor.service'
|
||||
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { Item } from '~/components/pub/my-ui/combobox'
|
||||
|
||||
const props = defineProps<{
|
||||
fieldName?: string
|
||||
label?: string
|
||||
placeholder?: string
|
||||
errors?: FormErrors
|
||||
class?: string
|
||||
selectClass?: string
|
||||
fieldGroupClass?: string
|
||||
labelClass?: string
|
||||
isRequired?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
fieldName = 'job',
|
||||
label = 'Pekerjaan',
|
||||
placeholder = 'Pilih pekerjaan',
|
||||
errors,
|
||||
class: containerClass,
|
||||
fieldGroupClass,
|
||||
labelClass,
|
||||
} = props
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
|
||||
// function handleDpjpChange(selected: string) {
|
||||
// selectedDpjpId.value = selected ?? null
|
||||
// }
|
||||
</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"
|
||||
:errors="errors"
|
||||
:class="cn('select-field-wrapper')"
|
||||
>
|
||||
<FormField
|
||||
v-slot="{ componentField }"
|
||||
:name="fieldName"
|
||||
>
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Combobox
|
||||
class="focus:ring-0 focus:ring-offset-0"
|
||||
:id="fieldName"
|
||||
v-bind="componentField"
|
||||
:items="PrbProgramTypeOptList"
|
||||
:placeholder="placeholder"
|
||||
search-placeholder="Cari..."
|
||||
empty-message="Data tidak ditemukan"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
</DE.Field>
|
||||
</DE.Cell>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import DetailRow from '~/components/pub/my-ui/form/view/detail-row.vue'
|
||||
import { type Variants, Badge } from '~/components/pub/ui/badge'
|
||||
import { cn, } from '~/lib/utils'
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { VaccineData } from '~/models/vaccine-data';
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
instance: VaccineData | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'click'): void
|
||||
}>()
|
||||
|
||||
const dummy = [
|
||||
{
|
||||
id: 1,
|
||||
number: 1,
|
||||
name: 'Operasi',
|
||||
code: 'OP-001'
|
||||
}
|
||||
]
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
// #endregion
|
||||
|
||||
// Computed addresses from nested data
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
function onClick() {
|
||||
emit('click')
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article :class="cn('mb-5 space-y-1',)">
|
||||
<DetailRow label="Jenis Vaksin">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Tanggal Pemberian Vaksin">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Tanggal Kedaluwarsa Vaksin">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Nomor Batch Vaksin">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Dosis Vaksin">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Dosis Ke">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Lokasi Injeksi">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
<DetailRow label="Tanggal Terlaksana">{{ props.instance?.date ? props.instance?.date : '-' }}</DetailRow>
|
||||
</article>
|
||||
|
||||
<div class="border-t-1 py-2 flex justify-end border-t-slate-300">
|
||||
<PubMyUiNavFooterBa @click="onClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import * as DE from '~/components/pub/my-ui/doc-entry'
|
||||
import type { FormErrors } from '~/types/error'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Form } from '~/components/pub/ui/form'
|
||||
import SelectDate from './_common/select-date.vue'
|
||||
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
|
||||
import SelectVaccineType from './_common/select-vaccine-type.vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
schema: any
|
||||
initialValues?: any
|
||||
errors?: FormErrors
|
||||
}>(), {
|
||||
|
||||
})
|
||||
|
||||
const formSchema = toTypedSchema(props.schema)
|
||||
const formRef = ref()
|
||||
|
||||
defineExpose({
|
||||
validate: () => formRef.value?.validate(),
|
||||
resetForm: () => formRef.value?.resetForm(),
|
||||
setValues: (values: any, shouldValidate = true) => formRef.value?.setValues(values, shouldValidate),
|
||||
values: computed(() => formRef.value?.values),
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
ref="formRef"
|
||||
v-slot="{ values }"
|
||||
as=""
|
||||
keep-values
|
||||
:validation-schema="formSchema"
|
||||
:validate-on-mount="false"
|
||||
validation-mode="onSubmit"
|
||||
:initial-values="initialValues ? initialValues : {}"
|
||||
>
|
||||
<DE.Block :col-count="3" :cell-flex="false">
|
||||
<SelectVaccineType :errors="errors"
|
||||
field-name="a1"
|
||||
label="Jenis Vaksin" placeholder="Pilih Jenis Vaksin"
|
||||
is-required
|
||||
/>
|
||||
<SelectDate
|
||||
field-name="a2"
|
||||
label="Tanggal Pemberian Vaksin"
|
||||
:errors="errors"
|
||||
is-with-time
|
||||
/>
|
||||
<SelectDate
|
||||
field-name="a3"
|
||||
label="Tanggal Kedaluwarsa Vaksin"
|
||||
:errors="errors"
|
||||
is-with-time
|
||||
/>
|
||||
</DE.Block>
|
||||
|
||||
<DE.Block :col-count="2" :cell-flex="false">
|
||||
<InputBase :errors="errors"
|
||||
field-name="a4"
|
||||
label="Nomor Batch Vaksin" placeholder="Isi Nomor Batch Vaksin"
|
||||
/>
|
||||
<InputBase :errors="errors"
|
||||
field-name="a5"
|
||||
label="Dosis Vaksin" placeholder="Isi Dosis Vaksin"
|
||||
right-label="ml"
|
||||
numeric-only
|
||||
/>
|
||||
<InputBase :errors="errors"
|
||||
field-name="a6"
|
||||
label="Dosis Ke" placeholder="Isi Dosis Ke"
|
||||
numeric-only
|
||||
/>
|
||||
<InputBase :errors="errors"
|
||||
field-name="a7"
|
||||
label="Lokasi Injeksi" placeholder="Isi Lokasi Injeksi"
|
||||
/>
|
||||
</DE.Block>
|
||||
</Form>
|
||||
</template>
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import actionDoctor from '~/components/pub/my-ui/data/dropdown-action-dd.vue'
|
||||
import actionNursePhysio from '~/components/pub/my-ui/data/dropdown-action-detail.vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dd.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {width: 50}, ],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Jenis Vaksin' },
|
||||
{ label: 'Tanggal Pemberian Vaksin' },
|
||||
{ label: 'Tanggal Kedaluwarsa Vaksin' },
|
||||
{ label: 'Dosis Ke' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['name1', 'date', 'date', 'name2', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
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: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Config } from '~/components/pub/my-ui/data-table'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import actionDoctor from '~/components/pub/my-ui/data/dropdown-action-dd.vue'
|
||||
import actionNursePhysio from '~/components/pub/my-ui/data/dropdown-action-detail.vue'
|
||||
|
||||
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-detail.vue'))
|
||||
|
||||
export const config: Config = {
|
||||
cols: [{}, {}, {}, {}, {width: 50}, ],
|
||||
|
||||
headers: [
|
||||
[
|
||||
{ label: 'Jenis Vaksin' },
|
||||
{ label: 'Tanggal Pemberian Vaksin' },
|
||||
{ label: 'Tanggal Kedaluwarsa Vaksin' },
|
||||
{ label: 'Dosis Ke' },
|
||||
{ label: 'Action' },
|
||||
],
|
||||
],
|
||||
|
||||
keys: ['name1', 'date', 'date', 'name2', 'action'],
|
||||
|
||||
delKeyNames: [
|
||||
{ key: 'code', label: 'Kode' },
|
||||
{ key: 'name', label: 'Nama' },
|
||||
],
|
||||
|
||||
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: {
|
||||
action(rec, idx) {
|
||||
return {
|
||||
idx,
|
||||
rec: rec as object,
|
||||
component: action,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
htmls: {
|
||||
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<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 as configDoctor } from './list-doctor.cfg'
|
||||
import { config as configNursePhysio } from './list-nurse-physio.cfg'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
data: any[]
|
||||
paginationMeta: PaginationMeta
|
||||
}>(), {
|
||||
|
||||
})
|
||||
|
||||
const { user, } = useUserStore()
|
||||
const tableConfig = computed(() => {
|
||||
if(user.activeRole === 'emp|doc' || user.activeRole === 'system') {
|
||||
return configDoctor
|
||||
} else {
|
||||
return configNursePhysio
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
pageChange: [page: number]
|
||||
}>()
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
emit('pageChange', page)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<PubMyUiDataTable
|
||||
v-bind="tableConfig"
|
||||
:rows="data"
|
||||
:skeleton-size="paginationMeta?.pageSize"
|
||||
/>
|
||||
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -26,6 +26,7 @@ import DocUploadList from '~/components/content/document-upload/list.vue'
|
||||
import GeneralConsentList from '~/components/content/general-consent/entry.vue'
|
||||
import ResumeList from '~/components/content/resume/list.vue'
|
||||
import ControlLetterList from '~/components/content/control-letter/list.vue'
|
||||
import VaccineDataList from '~/components/content/vaccine-data/list.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -96,6 +97,7 @@ const tabs: TabItem[] = [
|
||||
component: DocUploadList,
|
||||
props: { encounter: data },
|
||||
},
|
||||
{ value: 'vaccine-data', label: 'Data Vaksin', component: VaccineDataList, props: { encounter: data } },
|
||||
]
|
||||
</script>
|
||||
|
||||
|
||||
@@ -139,4 +139,4 @@ async function getData() {
|
||||
</div>
|
||||
<SubMenu :data="menus" :initial-active-menu="activeMenu" @change-menu="activeMenu = $event" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
@@ -0,0 +1,225 @@
|
||||
<script setup lang="ts">
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
// #region Imports
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||
import { getList, remove } from '~/services/prb.service'
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
import WarningAlert from '~/components/pub/my-ui/alert/warning-alert.vue'
|
||||
import type { Prb } from '~/models/prb'
|
||||
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
// #endregion
|
||||
|
||||
// #region State
|
||||
const props = withDefaults(defineProps<{
|
||||
encounter?: Encounter
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
const route = useRoute()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
|
||||
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params, includes: '', }),
|
||||
entityName: 'prb',
|
||||
})
|
||||
const prbHistory = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params }),
|
||||
entityName: 'prb-history',
|
||||
})
|
||||
const dummy = [
|
||||
{
|
||||
"id": 1,
|
||||
"date": new Date().toISOString(),
|
||||
"name1": "Dr. Smith",
|
||||
"name2": "Maria S.",
|
||||
"name3": "Project Alpha",
|
||||
"name4": "Completed",
|
||||
"name5": 95.5
|
||||
},
|
||||
]
|
||||
|
||||
const isHistoryDialogOpen = ref(false)
|
||||
const isDocPreviewDialogOpen = ref(false)
|
||||
const isFilterDialogOpen = ref(false)
|
||||
const isRecordConfirmationOpen = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const isRequirementsMet = ref(true)
|
||||
const Prb = ref<Prb | null>(null)
|
||||
|
||||
const recId = ref<number>(0)
|
||||
const recAction = ref<string>('')
|
||||
const recItem = ref<any>(null)
|
||||
const timestamp = ref<any>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: "Program Rujuk Balik",
|
||||
icon: 'i-lucide-history',
|
||||
}
|
||||
if(true){
|
||||
headerPrep.addNav = {
|
||||
label: "Program Rujuk Balik",
|
||||
onClick: () => navigateTo({
|
||||
name: 'integration-bpjs-prb-add',
|
||||
}),
|
||||
};
|
||||
}
|
||||
headerPrep.components = [
|
||||
{
|
||||
component: defineAsyncComponent(() => import('~/components/app/prb/_common/btn-history.vue')),
|
||||
props: { }
|
||||
},
|
||||
];
|
||||
const refSearchNav: RefSearchNav = {
|
||||
onClick: () => {
|
||||
isFilterDialogOpen.value = true
|
||||
},
|
||||
onInput: (val: string) => {
|
||||
searchInput.value = val
|
||||
},
|
||||
onClear: () => {
|
||||
searchInput.value = ''
|
||||
},
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(() => {
|
||||
getListData()
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
async function getListData() {
|
||||
try {
|
||||
summaryLoading.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
} catch (error) {
|
||||
console.error('Error fetching Data:', error)
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
function handleFiltering() {
|
||||
isFilterDialogOpen.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:
|
||||
navigateTo({
|
||||
name: 'integration-bpjs-prb-prb_id-edit',
|
||||
params: { id: encounterId, "prb_id": recId.value },
|
||||
})
|
||||
break
|
||||
|
||||
case ActionEvents.showPrint:
|
||||
isDocPreviewDialogOpen.value = true
|
||||
break
|
||||
|
||||
case ActionEvents.showConfirmDelete:
|
||||
isRecordConfirmationOpen.value = true
|
||||
break
|
||||
}
|
||||
})
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WarningAlert v-if="!isRequirementsMet"
|
||||
class="mb-5"
|
||||
text="Syarat pembuatan PRB belum terpenuhi"
|
||||
:description="[
|
||||
'Lanjutan Penatalaksanaan Pasien harus terisi Dirujuk Eksternal',
|
||||
'Jenis Pembayaran pasien harus JKN'
|
||||
]" />
|
||||
|
||||
<div v-else>
|
||||
<Header :prep="headerPrep" />
|
||||
|
||||
<Filter
|
||||
:prep="headerPrep"
|
||||
:ref-search-nav="refSearchNav"
|
||||
:enable-export="false"
|
||||
/>
|
||||
|
||||
<AppPrbList :is-bpjs="true" :data="dummy" />
|
||||
|
||||
<Dialog v-model:open="isHistoryDialogOpen" title="History" size="full">
|
||||
<AppPrbHistoryList
|
||||
:data="dummy"
|
||||
:pagination-meta="prbHistory.paginationMeta"
|
||||
@page-change="prbHistory.handlePageChange" />
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model:open="isFilterDialogOpen" title="Filter" size="lg">
|
||||
<AppPrbCommonFilter @submit="handleFiltering" />
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model:open="isDocPreviewDialogOpen" title="Preview Dokumen" size="2xl">
|
||||
<DocPreviewDialog :link="`https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf`" />
|
||||
</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>
|
||||
<p v-if="record?.code">
|
||||
<strong>Kode:</strong>
|
||||
{{ record.cellphone }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { withBase } from '~/models/_base'
|
||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import type { Person } from '~/models/person'
|
||||
import { getDetail } from '~/services/prb.service'
|
||||
|
||||
// Components
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import type { Prb } from '~/models/prb'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
const PrbId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
|
||||
const Prb = ref<Prb | null>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: 'Detail Program Rujuk Balik',
|
||||
icon: 'i-lucide-newspaper',
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// onMounted(async () => {
|
||||
// const result = await getDetail(controlLetterId, {
|
||||
// includes: "unit,specialist,subspecialist,doctor-employee-person",
|
||||
// })
|
||||
// if (result.success) {
|
||||
// controlLetter.value = result.body?.data
|
||||
// }
|
||||
// })
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
function goBack() {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
function handleAction() {
|
||||
goBack()
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header :prep="headerPrep" :ref-search-nav="headerPrep.refSearchNav" />
|
||||
<AppPrbDetail :instance="Prb" @click="handleAction" />
|
||||
</template>
|
||||
@@ -0,0 +1,160 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { Patient, genPatientProps } from '~/models/patient'
|
||||
import type { ExposedForm } from '~/types/form'
|
||||
import type { PatientBase } from '~/models/patient'
|
||||
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { genPatient } from '~/models/patient'
|
||||
import { PatientSchema } from '~/schemas/patient.schema'
|
||||
import { PersonAddressRelativeSchema } from '~/schemas/person-address-relative.schema'
|
||||
import { PersonAddressSchema } from '~/schemas/person-address.schema'
|
||||
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
|
||||
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
|
||||
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
|
||||
import { uploadAttachment } from '~/services/patient.service'
|
||||
import { getDetail, update } from '~/services/prb.service'
|
||||
import type { Prb } from '~/models/prb'
|
||||
import ActionDialog from '~/components/pub/my-ui/nav-footer/ba-su.vue'
|
||||
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import { withBase } from '~/models/_base'
|
||||
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
|
||||
import { PrbSchema } from '~/schemas/prb.schema'
|
||||
import { formatDateYyyyMmDd } from '~/lib/date'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import { getList, remove } from '~/services/prb.service'
|
||||
import { handleActionEdit } from '~/handlers/prb.handler'
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
import { formatDateToDatetimeLocal } from '~/lib/utils'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = withDefaults(defineProps<{
|
||||
callbackUrl?: string
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
|
||||
// form related state
|
||||
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params, includes: 'specialist,subspecialist,doctor-employee-person', }),
|
||||
entityName: 'prb',
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
const PrbId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
|
||||
|
||||
const inputForm = ref<ExposedForm<any> | null>(null)
|
||||
const Prb = ref({})
|
||||
const isConfirmationOpen = ref(false)
|
||||
const selectedOperativeAction = ref<any>(null)
|
||||
|
||||
const isSepDialogOpen = ref(false)
|
||||
provide("isSepDialogOpen", isSepDialogOpen);
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(async () => {
|
||||
const result = await getDetail(PrbId)
|
||||
if (result.success) {
|
||||
const responseData = {...result.body.data, date: formatDateYyyyMmDd(result.body.data.date)}
|
||||
Prb.value = responseData
|
||||
inputForm.value?.setValues(responseData)
|
||||
}
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
function goBack() {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
async function handleConfirmAdd() {
|
||||
const response = await handleActionEdit(
|
||||
PrbId,
|
||||
await composeFormData(),
|
||||
() => { },
|
||||
() => { },
|
||||
toast,
|
||||
)
|
||||
goBack()
|
||||
}
|
||||
|
||||
async function composeFormData(): Promise<Prb> {
|
||||
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) {
|
||||
await navigateTo(props.callbackUrl)
|
||||
return
|
||||
}
|
||||
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelAdd() {
|
||||
isConfirmationOpen.value = false
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
|
||||
const initialValues = {
|
||||
a1: "",
|
||||
a2: formatDateToDatetimeLocal(new Date()),
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Update Program Rujuk Balik</div>
|
||||
<AppPrbEntry
|
||||
ref="inputForm"
|
||||
:schema="PrbSchema"
|
||||
:initial-values="initialValues"
|
||||
:is-bpjs="isBpjs"
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
<style scoped>
|
||||
/* component style */
|
||||
</style>
|
||||
@@ -0,0 +1,202 @@
|
||||
<script setup lang="ts">
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
// #region Imports
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||
import { getList, remove } from '~/services/prb.service'
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
import WarningAlert from '~/components/pub/my-ui/alert/warning-alert.vue'
|
||||
import type { Prb } from '~/models/prb'
|
||||
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
// #endregion
|
||||
|
||||
// #region State
|
||||
const props = withDefaults(defineProps<{
|
||||
encounter?: Encounter
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
const route = useRoute()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
|
||||
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params, includes: '', }),
|
||||
entityName: 'prb',
|
||||
})
|
||||
const prbHistory = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params }),
|
||||
entityName: 'prb-history',
|
||||
})
|
||||
const dummy = [
|
||||
{
|
||||
"id": 1,
|
||||
"date": new Date().toISOString(),
|
||||
"name1": "Dr. Smith",
|
||||
"name2": "Maria S.",
|
||||
"name3": "Project Alpha",
|
||||
"name4": "Completed",
|
||||
"name5": 95.5
|
||||
},
|
||||
]
|
||||
|
||||
const isHistoryDialogOpen = ref(false)
|
||||
const isDocPreviewDialogOpen = ref(false)
|
||||
const isRecordConfirmationOpen = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const isRequirementsMet = ref(true)
|
||||
const Prb = ref<Prb | null>(null)
|
||||
|
||||
const recId = ref<number>(0)
|
||||
const recAction = ref<string>('')
|
||||
const recItem = ref<any>(null)
|
||||
const timestamp = ref<any>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: "Program Rujuk Balik",
|
||||
icon: 'i-lucide-history',
|
||||
}
|
||||
if(true){
|
||||
headerPrep.addNav = {
|
||||
label: "Program Rujuk Balik",
|
||||
onClick: () => navigateTo({
|
||||
name: 'rehab-encounter-id-prb-add',
|
||||
params: { id: encounterId },
|
||||
}),
|
||||
};
|
||||
}
|
||||
headerPrep.components = [
|
||||
{
|
||||
component: defineAsyncComponent(() => import('~/components/app/prb/_common/btn-history.vue')),
|
||||
props: { }
|
||||
},
|
||||
];
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(() => {
|
||||
getListData()
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
async function getListData() {
|
||||
try {
|
||||
summaryLoading.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
} catch (error) {
|
||||
console.error('Error fetching Data:', error)
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
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 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:
|
||||
navigateTo({
|
||||
name: 'rehab-encounter-id-prb-prb_id-edit',
|
||||
params: { id: encounterId, "prb_id": recId.value },
|
||||
})
|
||||
break
|
||||
|
||||
case ActionEvents.showPrint:
|
||||
isDocPreviewDialogOpen.value = true
|
||||
break
|
||||
|
||||
case ActionEvents.showConfirmDelete:
|
||||
isRecordConfirmationOpen.value = true
|
||||
break
|
||||
}
|
||||
})
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WarningAlert v-if="!isRequirementsMet"
|
||||
class="mb-5"
|
||||
text="Syarat pembuatan PRB belum terpenuhi"
|
||||
:description="[
|
||||
'Lanjutan Penatalaksanaan Pasien harus terisi Dirujuk Eksternal',
|
||||
'Jenis Pembayaran pasien harus JKN'
|
||||
]" />
|
||||
|
||||
<div v-else>
|
||||
<Header :prep="headerPrep" />
|
||||
|
||||
<AppPrbDetail :instance="Prb" />
|
||||
<h1 class="font-semibold text-lg mb-2">Obat</h1>
|
||||
<AppPrbList :is-bpjs="isBpjs" :data="dummy" />
|
||||
|
||||
<Dialog v-model:open="isHistoryDialogOpen" title="History" size="full">
|
||||
<AppPrbHistoryList
|
||||
:data="dummy"
|
||||
:pagination-meta="prbHistory.paginationMeta"
|
||||
@page-change="prbHistory.handlePageChange" />
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model:open="isDocPreviewDialogOpen" title="Preview Dokumen" size="2xl">
|
||||
<DocPreviewDialog :link="`https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf`" />
|
||||
</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>
|
||||
<p v-if="record?.code">
|
||||
<strong>Kode:</strong>
|
||||
{{ record.cellphone }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import { withBase } from '~/models/_base'
|
||||
import type { HeaderPrep } from '~/components/pub/my-ui/data/types'
|
||||
import type { Patient } from '~/models/patient'
|
||||
import type { Person } from '~/models/person'
|
||||
import { getDetail } from '~/services/vaccine-data.service'
|
||||
|
||||
// Components
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import type { VaccineData } from '~/models/vaccine-data'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = defineProps<{
|
||||
}>()
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
const VaccineDataId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
|
||||
const VaccineData = ref<VaccineData | null>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: 'Detail Data Vaksin',
|
||||
icon: 'i-lucide-newspaper',
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
// onMounted(async () => {
|
||||
// const result = await getDetail(controlLetterId, {
|
||||
// includes: "unit,specialist,subspecialist,doctor-employee-person",
|
||||
// })
|
||||
// if (result.success) {
|
||||
// controlLetter.value = result.body?.data
|
||||
// }
|
||||
// })
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
function goBack() {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// #endregion region
|
||||
|
||||
// #region Utilities & event handlers
|
||||
function handleAction() {
|
||||
goBack()
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Header :prep="headerPrep" :ref-search-nav="headerPrep.refSearchNav" />
|
||||
<AppVaccineDataDetail :instance="VaccineData" @click="handleAction" />
|
||||
</template>
|
||||
@@ -0,0 +1,159 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { Patient, genPatientProps } from '~/models/patient'
|
||||
import type { ExposedForm } from '~/types/form'
|
||||
import type { PatientBase } from '~/models/patient'
|
||||
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { genPatient } from '~/models/patient'
|
||||
import { PatientSchema } from '~/schemas/patient.schema'
|
||||
import { PersonAddressRelativeSchema } from '~/schemas/person-address-relative.schema'
|
||||
import { PersonAddressSchema } from '~/schemas/person-address.schema'
|
||||
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
|
||||
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
|
||||
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
|
||||
import { uploadAttachment } from '~/services/patient.service'
|
||||
import { getDetail, update } from '~/services/vaccine-data.service'
|
||||
import type { VaccineData } from '~/models/vaccine-data'
|
||||
import ActionDialog from '~/components/pub/my-ui/nav-footer/ba-su.vue'
|
||||
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import { withBase } from '~/models/_base'
|
||||
import Confirmation from '~/components/pub/my-ui/confirmation/confirmation.vue'
|
||||
import { VaccineDataSchema } from '~/schemas/vaccine-data.schema'
|
||||
import { formatDateYyyyMmDd } from '~/lib/date'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import { getList, remove } from '~/services/vaccine-data.service'
|
||||
import { handleActionEdit, handleActionSave } from '~/handlers/vaccine-data.handler'
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
import { formatDateToDatetimeLocal } from '~/lib/utils'
|
||||
|
||||
// #region Props & Emits
|
||||
const props = withDefaults(defineProps<{
|
||||
callbackUrl?: string
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
|
||||
// form related state
|
||||
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params, includes: 'specialist,subspecialist,doctor-employee-person', }),
|
||||
entityName: 'vaccine-data',
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region State & Computed
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
const VaccineDataId = typeof route.params.surgery_report_id == 'string' ? parseInt(route.params.surgery_report_id) : 0
|
||||
|
||||
const inputForm = ref<ExposedForm<any> | null>(null)
|
||||
const VaccineData = ref({})
|
||||
const isConfirmationOpen = ref(false)
|
||||
const selectedOperativeAction = ref<any>(null)
|
||||
|
||||
const isSepDialogOpen = ref(false)
|
||||
provide("isSepDialogOpen", isSepDialogOpen);
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(async () => {
|
||||
const result = await getDetail(VaccineDataId)
|
||||
if (result.success) {
|
||||
const responseData = {...result.body.data, date: formatDateYyyyMmDd(result.body.data.date)}
|
||||
VaccineData.value = responseData
|
||||
inputForm.value?.setValues(responseData)
|
||||
}
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
function goBack() {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
async function handleConfirmAdd() {
|
||||
const response = await handleActionSave(
|
||||
await composeFormData(),
|
||||
() => { },
|
||||
() => { },
|
||||
toast,
|
||||
)
|
||||
goBack()
|
||||
}
|
||||
|
||||
async function composeFormData(): Promise<VaccineData> {
|
||||
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) {
|
||||
await navigateTo(props.callbackUrl)
|
||||
return
|
||||
}
|
||||
|
||||
goBack()
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelAdd() {
|
||||
isConfirmationOpen.value = false
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Watchers
|
||||
// #endregion
|
||||
|
||||
const initialValues = {
|
||||
a1: "",
|
||||
a2: formatDateToDatetimeLocal(new Date()),
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">Update Data Vaksin</div>
|
||||
<AppVaccineDataEntry
|
||||
ref="inputForm"
|
||||
:schema="VaccineDataSchema"
|
||||
:initial-values="initialValues"
|
||||
:is-bpjs="isBpjs"
|
||||
/>
|
||||
|
||||
<div class="my-2 flex justify-end py-2">
|
||||
<Action @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>
|
||||
|
||||
<style scoped>
|
||||
/* component style */
|
||||
</style>
|
||||
@@ -0,0 +1,165 @@
|
||||
<script setup lang="ts">
|
||||
import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
// #region Imports
|
||||
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
|
||||
import { ActionEvents } from '~/components/pub/my-ui/data/types'
|
||||
|
||||
import Filter from '~/components/pub/my-ui/nav-header/filter.vue'
|
||||
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
|
||||
import { usePaginatedList } from '~/composables/usePaginatedList'
|
||||
import { getList, remove } from '~/services/vaccine-data.service'
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
import type { Encounter } from '~/models/encounter'
|
||||
import WarningAlert from '~/components/pub/my-ui/alert/warning-alert.vue'
|
||||
import DocPreviewDialog from '~/components/pub/my-ui/modal/doc-preview-dialog.vue'
|
||||
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
|
||||
import type { VaccineData } from '~/models/vaccine-data'
|
||||
import { medicalRoles } from '~/const/common/role'
|
||||
// #endregion
|
||||
|
||||
// #region State
|
||||
const props = withDefaults(defineProps<{
|
||||
encounter?: Encounter
|
||||
isBpjs?: boolean
|
||||
}>(), {
|
||||
isBpjs: false,
|
||||
})
|
||||
const route = useRoute()
|
||||
const {user} = useUserStore()
|
||||
const encounterId = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
|
||||
|
||||
const { data, isLoading, paginationMeta, searchInput, handlePageChange, handleSearch, fetchData } = usePaginatedList({
|
||||
fetchFn: (params) => getList({ ...params, includes: '', }),
|
||||
entityName: 'vaccine-data',
|
||||
})
|
||||
const dummy = [
|
||||
{
|
||||
"id": 1,
|
||||
"date": new Date().toISOString(),
|
||||
"name1": "Dr. Smith",
|
||||
"name2": 1,
|
||||
},
|
||||
]
|
||||
|
||||
const isHistoryDialogOpen = ref(false)
|
||||
const isDocPreviewDialogOpen = ref(false)
|
||||
const isRecordConfirmationOpen = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const isRequirementsMet = ref(true)
|
||||
const vaccineData = ref<VaccineData | null>(null)
|
||||
|
||||
const recId = ref<number>(0)
|
||||
const recAction = ref<string>('')
|
||||
const recItem = ref<any>(null)
|
||||
const timestamp = ref<any>(null)
|
||||
|
||||
const headerPrep: HeaderPrep = {
|
||||
title: "Data Vaksin",
|
||||
icon: 'i-lucide-syringe',
|
||||
}
|
||||
if(user.activeRole === 'emp|doc' || user.activeRole === 'system') {
|
||||
headerPrep.addNav = {
|
||||
label: "Data Vaksin",
|
||||
onClick: () => navigateTo({
|
||||
name: 'rehab-encounter-id-vaccine-data-add',
|
||||
params: { id: encounterId },
|
||||
}),
|
||||
};
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle Hooks
|
||||
onMounted(() => {
|
||||
getListData()
|
||||
})
|
||||
// #endregion
|
||||
|
||||
// #region Functions
|
||||
async function getListData() {
|
||||
try {
|
||||
summaryLoading.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
} catch (error) {
|
||||
console.error('Error fetching Data:', error)
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
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 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.showDetail:
|
||||
navigateTo({
|
||||
name: 'rehab-encounter-id-vaccine-data-vaccine_data_id',
|
||||
params: { id: encounterId, "vaccine_data_id": recId.value },
|
||||
})
|
||||
break
|
||||
|
||||
case ActionEvents.showConfirmDelete:
|
||||
isRecordConfirmationOpen.value = true
|
||||
break
|
||||
}
|
||||
})
|
||||
// #endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Header :prep="headerPrep" />
|
||||
|
||||
<AppVaccineDataList
|
||||
:data="dummy"
|
||||
:pagination-meta="paginationMeta"
|
||||
@page-change="handlePageChange" />
|
||||
|
||||
<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?.name">
|
||||
<strong>Nama:</strong>
|
||||
{{ record.name }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</RecordConfirmation>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type LinkItem, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
|
||||
|
||||
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 timestamp = inject<Ref<any>>('timestamp')!
|
||||
|
||||
const activeKey = ref<string | null>(null)
|
||||
|
||||
function process() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showProcess
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button @click="process" variant="outline"
|
||||
class="text-orange-400 border-orange-400 bg-transparent">
|
||||
Pilih
|
||||
<Icon name="i-lucide-arrow-right" class="h-4 w-4 align-middle transition-colors" />
|
||||
</Button>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { ActionEvents, type LinkItem, type ListItemDto } from '~/components/pub/my-ui/data/types';
|
||||
|
||||
|
||||
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 timestamp = inject<Ref<any>>('timestamp')!
|
||||
|
||||
const activeKey = ref<string | null>(null)
|
||||
|
||||
function process() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showProcess
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button @click="process" variant="outline"
|
||||
class="text-orange-400 border-orange-400 bg-transparent">
|
||||
Detail
|
||||
<Icon name="i-lucide-search" class="h-4 w-4 align-middle transition-colors" />
|
||||
</Button>
|
||||
</template>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import type { LinkItem, ListItemDto } from './types'
|
||||
import { ActionEvents } from './types'
|
||||
|
||||
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 timestamp = inject<Ref<any>>('timestamp')!
|
||||
|
||||
function print() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showPrint
|
||||
recItem.value = props.rec
|
||||
timestamp.value = new Date().getTime()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Button type="button" variant="outline"
|
||||
class="text-orange-500 border border-orange-400"
|
||||
@click="print">
|
||||
<Icon name="i-lucide-printer" class="h-4 w-4 align-middle transition-colors" />
|
||||
Preview
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import type { LinkItem, ListItemDto } from './types'
|
||||
import { ActionEvents } from './types'
|
||||
|
||||
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 timestamp = inject<Ref<any>>('timestamp')!
|
||||
|
||||
const activeKey = ref<string | null>(null)
|
||||
const linkItems: LinkItem[] = [
|
||||
{
|
||||
label: 'Edit',
|
||||
onClick: () => {
|
||||
edit()
|
||||
},
|
||||
icon: 'i-lucide-pencil',
|
||||
},
|
||||
{
|
||||
label: 'Print',
|
||||
onClick: () => {
|
||||
print()
|
||||
},
|
||||
icon: 'i-lucide-printer',
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
onClick: () => {
|
||||
del()
|
||||
},
|
||||
icon: 'i-lucide-trash',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
function edit() {
|
||||
recId.value = props.rec.id || 0
|
||||
recAction.value = ActionEvents.showEdit
|
||||
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,24 @@
|
||||
// Handlers
|
||||
import { genCrudHandler } from '~/handlers/_handler'
|
||||
|
||||
// Services
|
||||
import { create, update, remove } from '~/services/prb.service'
|
||||
|
||||
export const {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} = genCrudHandler({
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
// Handlers
|
||||
import { genCrudHandler } from '~/handlers/_handler'
|
||||
|
||||
// Services
|
||||
import { create, update, remove } from '~/services/vaccine-data.service'
|
||||
|
||||
export const {
|
||||
recId,
|
||||
recAction,
|
||||
recItem,
|
||||
isReadonly,
|
||||
isProcessing,
|
||||
isFormEntryDialogOpen,
|
||||
isRecordConfirmationOpen,
|
||||
onResetState,
|
||||
handleActionSave,
|
||||
handleActionEdit,
|
||||
handleActionRemove,
|
||||
handleCancelForm,
|
||||
} = genCrudHandler({
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
})
|
||||
@@ -486,4 +486,16 @@ export const SpecimenTypeOptList: { label: string; value: SpecimenType }[] = [
|
||||
{ label: 'Mikrobiologi', value: 'mikrobiologi' },
|
||||
{ label: 'Laborat', value: 'laborat' },
|
||||
{ label: 'Tidak perlu', value: 'tidak perlu' },
|
||||
]
|
||||
|
||||
export type PrbProgramType = "ashma" | "diabetes mellitus" | "hipertensi" | "penyakit jantung" | "ppok" | "schizopherenia" | "stroke" | "systemic lupus erythematosus"
|
||||
export const PrbProgramTypeOptList: { label: string; value: PrbProgramType }[] = [
|
||||
{ label: 'ASHMA', value: 'ashma' },
|
||||
{ label: 'Diabetes Mellitus', value: 'diabetes mellitus' },
|
||||
{ label: 'Hipertensi', value: 'hipertensi' },
|
||||
{ label: 'Penyakit Jantung', value: 'penyakit jantung' },
|
||||
{ label: 'PPOK (Penyakit Paru Obstruktif Kronik)', value: 'ppok' },
|
||||
{ label: 'Schizopherenia', value: 'schizopherenia' },
|
||||
{ label: 'Stroke', value: 'stroke' },
|
||||
{ label: 'Systemic Lupus Erythematosus', value: 'systemic lupus erythematosus' },
|
||||
]
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ClassValue } from 'clsx'
|
||||
import { clsx } from 'clsx'
|
||||
import { format } from 'date-fns'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { toast } from '~/components/pub/ui/toast'
|
||||
|
||||
@@ -106,6 +107,11 @@ export function calculateAge(birthDate: Date | string | null | undefined): strin
|
||||
}
|
||||
}
|
||||
|
||||
export function formatDateToDatetimeLocal(inputDate: Date) {
|
||||
const formattedString = format(inputDate, "yyyy-MM-dd'T'HH:mm")
|
||||
return formattedString
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a plain JavaScript object (including File objects) into a FormData instance.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import { genDoctor, type Doctor } from "./doctor"
|
||||
import { genEncounter, type Encounter } from "./encounter"
|
||||
import { genSpecialist, type Specialist } from "./specialist"
|
||||
import { genSubspecialist, type Subspecialist } from "./subspecialist"
|
||||
import { genUnit, type Unit } from "./unit"
|
||||
|
||||
export interface Prb extends Base {
|
||||
encounter_id: number
|
||||
encounter: Encounter
|
||||
unit_id: number
|
||||
unit: Unit
|
||||
specialist_id: number
|
||||
specialist: Specialist
|
||||
subspecialist_id: number
|
||||
subspecialist: Subspecialist
|
||||
doctor_id: number
|
||||
doctor: Doctor
|
||||
date: ''
|
||||
}
|
||||
|
||||
export function genPrb(): Prb {
|
||||
return {
|
||||
...genBase(),
|
||||
encounter_id: 0,
|
||||
encounter: genEncounter(),
|
||||
unit_id: 0,
|
||||
unit: genUnit(),
|
||||
specialist_id: 0,
|
||||
specialist: genSpecialist(),
|
||||
subspecialist_id: 0,
|
||||
subspecialist: genSubspecialist(),
|
||||
doctor_id: 0,
|
||||
doctor: genDoctor(),
|
||||
date: ''
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import { genDoctor, type Doctor } from "./doctor"
|
||||
import { genEncounter, type Encounter } from "./encounter"
|
||||
import { genSpecialist, type Specialist } from "./specialist"
|
||||
import { genSubspecialist, type Subspecialist } from "./subspecialist"
|
||||
import { genUnit, type Unit } from "./unit"
|
||||
|
||||
export interface VaccineData extends Base {
|
||||
encounter_id: number
|
||||
encounter: Encounter
|
||||
unit_id: number
|
||||
unit: Unit
|
||||
specialist_id: number
|
||||
specialist: Specialist
|
||||
subspecialist_id: number
|
||||
subspecialist: Subspecialist
|
||||
doctor_id: number
|
||||
doctor: Doctor
|
||||
date: ''
|
||||
}
|
||||
|
||||
export function genVaccineData(): VaccineData {
|
||||
return {
|
||||
...genBase(),
|
||||
encounter_id: 0,
|
||||
encounter: genEncounter(),
|
||||
unit_id: 0,
|
||||
unit: genUnit(),
|
||||
specialist_id: 0,
|
||||
specialist: genSpecialist(),
|
||||
subspecialist_id: 0,
|
||||
subspecialist: genSubspecialist(),
|
||||
doctor_id: 0,
|
||||
doctor: genDoctor(),
|
||||
date: ''
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Update PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
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)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentPrbEntry :is-bpjs="true" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Detail PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
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)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentPrbDetail :patient-id="Number(route.params.id)" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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 PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||
|
||||
const { checkRole, getPagePermissions } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
const pagePermission = getPagePermissions(roleAccess)
|
||||
const callbackUrl = route.query['return-path'] as string | undefined
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="pagePermission.canRead">
|
||||
<ContentPrbEntry :is-bpjs="true" :callback-url="callbackUrl" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Daftar PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||
|
||||
const { checkRole, getPagePermissions } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
const pagePermission = getPagePermissions(roleAccess)
|
||||
const callbackUrl = route.query['return-path'] as string | undefined
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="pagePermission.canRead">
|
||||
<ContentPrbBpjsList :callback-url="callbackUrl" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Update PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
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)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentPrbEntry />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Detail PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
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)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentPrbDetail :patient-id="Number(route.params.id)" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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 PRB',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||
|
||||
const { checkRole, getPagePermissions } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
const pagePermission = getPagePermissions(roleAccess)
|
||||
const callbackUrl = route.query['return-path'] as string | undefined
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="pagePermission.canRead">
|
||||
<ContentPrbEntry :callback-url="callbackUrl" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { PagePermission } from '~/models/role'
|
||||
import Error from '~/components/pub/my-ui/error/error.vue'
|
||||
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||
import EncounterProcess from '~/components/content/encounter/process.vue'
|
||||
import EncounterProcess from '~/components/content/encounter/process-bu.vue'
|
||||
|
||||
definePageMeta({
|
||||
middleware: ['rbac'],
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<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: 'Detail Data Vaksin',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
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)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
// const canRead = hasReadAccess(roleAccess)
|
||||
const canRead = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="canRead">
|
||||
<ContentVaccineDataDetail :patient-id="Number(route.params.id)" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
<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 Data Vaksin',
|
||||
contentFrame: 'cf-full-width',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: () => route.meta.title as string,
|
||||
})
|
||||
|
||||
const roleAccess: PagePermission = PAGE_PERMISSIONS['/rehab/encounter']
|
||||
|
||||
const { checkRole, getPagePermissions } = useRBAC()
|
||||
|
||||
// Check if user has access to this page
|
||||
const hasAccess = checkRole(roleAccess)
|
||||
// if (!hasAccess) {
|
||||
// navigateTo('/403')
|
||||
// }
|
||||
|
||||
// Define permission-based computed properties
|
||||
const pagePermission = getPagePermissions(roleAccess)
|
||||
const callbackUrl = route.query['return-path'] as string | undefined
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="pagePermission.canRead">
|
||||
<ContentVaccineDataEntry :callback-url="callbackUrl" />
|
||||
</div>
|
||||
<Error v-else :status-code="403" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const PrbMedicineSchema = z.object({
|
||||
a1: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}).optional(),
|
||||
a2: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}),
|
||||
a3: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}),
|
||||
a4: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}).optional(),
|
||||
a5: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}),
|
||||
a6: z.string({
|
||||
required_error: 'Mohon isi',
|
||||
}),
|
||||
})
|
||||
|
||||
type PrbMedicineFormData = z.infer<typeof PrbMedicineSchema>
|
||||
|
||||
export { PrbMedicineSchema }
|
||||
export type { PrbMedicineFormData }
|
||||
@@ -0,0 +1,47 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const PrbSchema = z.object({
|
||||
sepStatus: z.string({
|
||||
required_error: 'Mohon isi status SEP',
|
||||
}).default('SEP Internal'),
|
||||
unit_code: z.string({
|
||||
required_error: 'Mohon isi Unit',
|
||||
}),
|
||||
specialist_code: z.string({
|
||||
required_error: 'Mohon isi Spesialis',
|
||||
}),
|
||||
subspecialist_code: z.string({
|
||||
required_error: 'Mohon isi Sub Spesialis',
|
||||
}),
|
||||
doctor_code: z.string({
|
||||
required_error: 'Mohon isi DPJP',
|
||||
}),
|
||||
encounter_code: z.string().optional(),
|
||||
date: z.string({
|
||||
required_error: 'Mohon lengkapi Tanggal Kontrol',
|
||||
})
|
||||
.refine(
|
||||
(date) => {
|
||||
// Jika kosong, return false untuk required validation
|
||||
if (!date || date.trim() === '') return false
|
||||
|
||||
// Jika ada isi, validasi format tanggal
|
||||
try {
|
||||
const dateObj = new Date(date)
|
||||
// Cek apakah tanggal valid dan tahun >= 1900
|
||||
return !isNaN(dateObj.getTime()) && dateObj.getFullYear() >= 1900
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{
|
||||
message: 'Mohon lengkapi Tanggal Kontrol dengan format yang valid',
|
||||
},
|
||||
)
|
||||
.transform((dateStr) => new Date(dateStr).toISOString()),
|
||||
})
|
||||
|
||||
type PrbFormData = z.infer<typeof PrbSchema>
|
||||
|
||||
export { PrbSchema }
|
||||
export type { PrbFormData }
|
||||
@@ -0,0 +1,47 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
const VaccineDataSchema = z.object({
|
||||
sepStatus: z.string({
|
||||
required_error: 'Mohon isi status SEP',
|
||||
}).default('SEP Internal'),
|
||||
unit_code: z.string({
|
||||
required_error: 'Mohon isi Unit',
|
||||
}),
|
||||
specialist_code: z.string({
|
||||
required_error: 'Mohon isi Spesialis',
|
||||
}),
|
||||
subspecialist_code: z.string({
|
||||
required_error: 'Mohon isi Sub Spesialis',
|
||||
}),
|
||||
doctor_code: z.string({
|
||||
required_error: 'Mohon isi DPJP',
|
||||
}),
|
||||
encounter_code: z.string().optional(),
|
||||
date: z.string({
|
||||
required_error: 'Mohon lengkapi Tanggal Kontrol',
|
||||
})
|
||||
.refine(
|
||||
(date) => {
|
||||
// Jika kosong, return false untuk required validation
|
||||
if (!date || date.trim() === '') return false
|
||||
|
||||
// Jika ada isi, validasi format tanggal
|
||||
try {
|
||||
const dateObj = new Date(date)
|
||||
// Cek apakah tanggal valid dan tahun >= 1900
|
||||
return !isNaN(dateObj.getTime()) && dateObj.getFullYear() >= 1900
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
{
|
||||
message: 'Mohon lengkapi Tanggal Kontrol dengan format yang valid',
|
||||
},
|
||||
)
|
||||
.transform((dateStr) => new Date(dateStr).toISOString()),
|
||||
})
|
||||
|
||||
type VaccineDataFormData = z.infer<typeof VaccineDataSchema>
|
||||
|
||||
export { VaccineDataSchema }
|
||||
export type { VaccineDataFormData }
|
||||
@@ -1,4 +1,5 @@
|
||||
// Base
|
||||
import type { Medicine } from '~/models/medicine'
|
||||
import * as base from './_crud-base'
|
||||
|
||||
const path = '/api/v1/medicine'
|
||||
@@ -23,3 +24,16 @@ export function update(id: number | string, data: any) {
|
||||
export function remove(id: number | string) {
|
||||
return base.remove(path, id, name)
|
||||
}
|
||||
|
||||
export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> {
|
||||
let data: { value: string; label: string }[] = []
|
||||
const result = await getList(params)
|
||||
if (result.success) {
|
||||
const resultData = result.body?.data || []
|
||||
data = resultData.map((item: Medicine) => ({
|
||||
value: item.code,
|
||||
label: item.name,
|
||||
}))
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Base
|
||||
import * as base from './_crud-base'
|
||||
|
||||
// Constants
|
||||
import { encounterClassCodes } from '~/lib/constants'
|
||||
|
||||
const path = '/api/v1/prb'
|
||||
const name = 'prb'
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Base
|
||||
import * as base from './_crud-base'
|
||||
|
||||
// Constants
|
||||
import { encounterClassCodes } from '~/lib/constants'
|
||||
|
||||
const path = '/api/v1/vaccine-data'
|
||||
const name = 'vaccine-data'
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -200,6 +200,11 @@
|
||||
"icon": "i-lucide-circuit-board",
|
||||
"link": "/integration/bpjs-vclaim/member"
|
||||
},
|
||||
{
|
||||
"title": "PRB",
|
||||
"icon": "i-lucide-circuit-board",
|
||||
"link": "/integration/bpjs/prb"
|
||||
},
|
||||
{
|
||||
"title": "Surat Kontrol",
|
||||
"icon": "i-lucide-circuit-board",
|
||||
|
||||
Reference in New Issue
Block a user