Merge branch 'dev' into fe-prescription-56

This commit is contained in:
Andrian Roshandy
2025-09-29 08:27:01 +07:00
47 changed files with 1267 additions and 566 deletions
-1
View File
@@ -20,7 +20,6 @@ useHead({
<NuxtPage /> <NuxtPage />
</NuxtLayout> </NuxtLayout>
</div> </div>
<Toaster /> <Toaster />
</ConfigProvider> </ConfigProvider>
</template> </template>
+5 -1
View File
@@ -170,7 +170,11 @@ body {
} }
body { body {
@apply bg-slate-100 dark:bg-slate-800; @apply bg-slate-100 dark:bg-slate-800 ;
}
body, table, label {
@apply md:!text-xs xl:!text-sm 2xl:!text-base;
} }
/* Container */ /* Container */
+1 -1
View File
@@ -38,7 +38,7 @@ const items = [
</Field> </Field>
</FieldGroup> </FieldGroup>
<FieldGroup :column="2"> <FieldGroup :column="2">
<Label>Metode Pemberian</Label> <Label>Cara Pemberian</Label>
<Field name="phone"> <Field name="phone">
<Select :items="items" /> <Select :items="items" />
</Field> </Field>
@@ -0,0 +1,66 @@
import type {
Col,
KeyLabel,
RecComponent,
RecStrFuncComponent,
RecStrFuncUnknown,
Th,
} from '~/components/pub/custom-ui/data/types'
import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/custom-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [{}, {}, {}, {}, {}, {}, { width: 50 }]
export const header: Th[][] = [
[
{ label: 'Nama' },
{ label: 'Bentuk' },
{ label: 'Freq' },
{ label: 'Dosis' },
{ label: 'Interval' },
{ label: 'Total' },
{ label: '' },
],
]
export const keys = ['name', 'uom_code', 'frequency', 'multiplier', 'interval', 'total', 'action']
export const delKeyNames: KeyLabel[] = [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
]
export const funcParsed: RecStrFuncUnknown = {
cateogry: (rec: unknown): unknown => {
return (rec as SmallDetailDto).medicineCategory?.name || '-'
},
group: (rec: unknown): unknown => {
return (rec as SmallDetailDto).medicineGroup?.name || '-'
},
method: (rec: unknown): unknown => {
return (rec as SmallDetailDto).medicineMethod?.name || '-'
},
unit: (rec: unknown): unknown => {
return (rec as SmallDetailDto).medicineUnit?.name || '-'
},
}
export const funcComponent: RecStrFuncComponent = {
action: (rec: unknown, idx: number): RecComponent => {
const res: RecComponent = {
idx,
rec: rec as object,
component: action,
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {
// (_rec) {
// return '-'
// },
}
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-entry'
defineProps<{
data: any[]
}>()
</script>
<template>
<PubBaseDataTable
:rows="data"
:cols="cols"
:header="header"
:keys="keys"
:func-parsed="funcParsed"
:func-html="funcHtml"
:func-component="funcComponent"
/>
</template>
+32
View File
@@ -0,0 +1,32 @@
<template>
<div>
<PubCustomUiDocEntryBlock mode="preview" :colCount=3>
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>DPJP</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell />
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>Tgl Order</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>DPJP</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell />
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>Status</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
</PubCustomUiDocEntryBlock>
</div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<template>
<div class="p-10 text-center">
<div class="mb-4 xl:mb-5">Belum Ada Data</div>
<div>
<Button>
<Icon name="i-lucide-plus" class="me-2 align-middle" />
Tambah Order
</Button>
</div>
</div>
<Separator class="my-5" />
<div>
<PubCustomUiDocEntryBlock mode="preview" :colCount=3>
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>DPJP</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell />
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>Tgl Order</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>DPJP</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
<PubCustomUiDocEntryCell />
<PubCustomUiDocEntryCell>
<PubCustomUiDocEntryLabel>Status</PubCustomUiDocEntryLabel>
<PubCustomUiDocEntryField>
<Input />
</PubCustomUiDocEntryField>
</PubCustomUiDocEntryCell>
</PubCustomUiDocEntryBlock>
</div>
</template>
+21 -23
View File
@@ -176,33 +176,31 @@ function handleCancelConfirmation() {
</script> </script>
<template> <template>
<div class="rounded-md border p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppDivisonList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppDivisonList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Divisi" size="lg" prevent-outside> <Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Divisi" size="lg" prevent-outside>
<AppDivisionEntryForm <AppDivisionEntryForm
:division="divisionConf" :division-tree="divisionTreeConfig" :schema="schema" :division="divisionConf" :division-tree="divisionTreeConfig" :schema="schema"
:initial-values="{ name: '', code: '', parentId: '' }" @submit="onSubmitForm" @cancel="onCancelForm" :initial-values="{ name: '', code: '', parentId: '' }" @submit="onSubmitForm" @cancel="onCancelForm"
/> />
</Dialog> </Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem" v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem"
@confirm="handleConfirmDelete" @cancel="handleCancelConfirmation" @confirm="handleConfirmDelete" @cancel="handleCancelConfirmation"
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p> <p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
<style scoped> <style scoped>
+41 -45
View File
@@ -130,52 +130,48 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppEquipmentList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<div class="rounded-md border p-4">
<AppEquipmentList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
<Dialog <Dialog
v-model:open="isFormEntryDialogOpen" v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Perlengkapan'" :title="!!recItem ? title : 'Tambah Perlengkapan'"
size="lg" size="lg"
prevent-outside prevent-outside
> >
<AppEquipmentEntryForm <AppEquipmentEntryForm
:schema="MaterialSchema" :schema="MaterialSchema"
:values="recItem" :values="recItem"
:uoms="uoms" :uoms="uoms"
:is-loading="isProcessing" :is-loading="isProcessing"
:is-readonly="isReadonly" :is-readonly="isReadonly"
@submit=" @submit="
(values: MaterialFormData, resetForm: any) => { (values: MaterialFormData, resetForm: any) => {
if (recId > 0) { if (recId > 0) {
handleActionEdit(recId, values, getEquipmentList, resetForm, toast) handleActionEdit(recId, values, getEquipmentList, resetForm, toast)
return return
}
handleActionSave(values, getEquipmentList, resetForm, toast)
} }
" handleActionSave(values, getEquipmentList, resetForm, toast)
@cancel="handleCancelForm" }
/> "
</Dialog> @cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="() => handleActionRemove(recId, getEquipmentList, toast)" @confirm="() => handleActionRemove(recId, getEquipmentList, toast)"
@cancel="" @cancel=""
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p> <p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
+18 -20
View File
@@ -179,28 +179,26 @@ function handleCancelConfirmation() {
</script> </script>
<template> <template>
<div class="rounded-md border p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppInstallationList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppInstallationList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Instalasi" size="lg" prevent-outside> <Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Instalasi" size="lg" prevent-outside>
<AppInstallationEntryForm :installation="installationConf" :schema="schemaConf" <AppInstallationEntryForm :installation="installationConf" :schema="schemaConf"
:initial-values="{ name: '', code: '', encounterClassCode: '' }" @submit="onSubmitForm" :initial-values="{ name: '', code: '', encounterClassCode: '' }" @submit="onSubmitForm"
@cancel="onCancelForm" /> @cancel="onCancelForm" />
</Dialog> </Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem" <RecordConfirmation v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem"
@confirm="handleConfirmDelete" @cancel="handleCancelConfirmation"> @confirm="handleConfirmDelete" @cancel="handleCancelConfirmation">
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p> <p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
<style scoped> <style scoped>
+40 -44
View File
@@ -112,51 +112,47 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppMedicineGroupList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<div class="rounded-md border p-4">
<AppMedicineGroupList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
<Dialog <Dialog
v-model:open="isFormEntryDialogOpen" v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Kelompok Obat'" :title="!!recItem ? title : 'Tambah Kelompok Obat'"
size="lg" size="lg"
prevent-outside prevent-outside
> >
<AppMedicineGroupEntryForm <AppMedicineGroupEntryForm
:schema="MedicineBaseSchema" :schema="MedicineBaseSchema"
:values="recItem" :values="recItem"
:is-loading="isProcessing" :is-loading="isProcessing"
:is-readonly="isReadonly" :is-readonly="isReadonly"
@submit=" @submit="
(values: MedicineBaseFormData | Record<string, any>, resetForm: () => void) => { (values: MedicineBaseFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) { if (recId > 0) {
handleActionEdit(recId, values, getMedicineGroupList, resetForm, toast) handleActionEdit(recId, values, getMedicineGroupList, resetForm, toast)
return return
}
handleActionSave(values, getMedicineGroupList, resetForm, toast)
} }
" handleActionSave(values, getMedicineGroupList, resetForm, toast)
@cancel="handleCancelForm" }
/> "
</Dialog> @cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="() => handleActionRemove(recId, getMedicineGroupList, toast)" @confirm="() => handleActionRemove(recId, getMedicineGroupList, toast)"
@cancel="" @cancel=""
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p> <p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
+40 -44
View File
@@ -112,51 +112,47 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppMedicineMethodList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<div class="rounded-md border p-4">
<AppMedicineMethodList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
<Dialog <Dialog
v-model:open="isFormEntryDialogOpen" v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Metode Obat'" :title="!!recItem ? title : 'Tambah Metode Obat'"
size="lg" size="lg"
prevent-outside prevent-outside
> >
<AppMedicineMethodEntryForm <AppMedicineMethodEntryForm
:schema="MedicineBaseSchema" :schema="MedicineBaseSchema"
:values="recItem" :values="recItem"
:is-loading="isProcessing" :is-loading="isProcessing"
:is-readonly="isReadonly" :is-readonly="isReadonly"
@submit=" @submit="
(values: MedicineBaseFormData | Record<string, any>, resetForm: () => void) => { (values: MedicineBaseFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) { if (recId > 0) {
handleActionEdit(recId, values, getMedicineMethodList, resetForm, toast) handleActionEdit(recId, values, getMedicineMethodList, resetForm, toast)
return return
}
handleActionSave(values, getMedicineMethodList, resetForm, toast)
} }
" handleActionSave(values, getMedicineMethodList, resetForm, toast)
@cancel="handleCancelForm" }
/> "
</Dialog> @cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="() => handleActionRemove(recId, getMedicineMethodList, toast)" @confirm="() => handleActionRemove(recId, getMedicineMethodList, toast)"
@cancel="" @cancel=""
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p> <p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
@@ -0,0 +1,69 @@
<script setup lang="ts">
import type { DataTableLoader } from '~/components/pub/base/data-table/type'
import type { HeaderPrep, RefSearchNav } from '~/components/pub/custom-ui/data/types'
import Header from '~/components/pub/custom-ui/nav-header/prep.vue'
import PrescriptionItemListEntry from '~/components/app/prescription-item/list-entry.vue'
const data = ref([])
const refSearchNav: RefSearchNav = {
onClick: () => {
// open filter modal
},
onInput: (_val: string) => {
// filter patient list
},
onClear: () => {
// clear url param
},
}
const isLoading = reactive<DataTableLoader>({
isTableLoading: false,
})
const recId = ref<number>(0)
const recAction = ref<string>('')
const recItem = ref<any>(null)
const headerPrep: HeaderPrep = {
title: 'Resep Obat',
icon: 'i-lucide-panel-bottom',
addNav: {
label: 'Tambah',
onClick: () => navigateTo('/tools-equipment-src/equipment/add'),
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
onMounted(() => {
getMaterialList()
})
async function getMaterialList() {
isLoading.dataListLoading = true
// const resp = await xfetch('/api/v1/material')
// if (resp.success) {
// data.value = (resp.body as Record<string, any>).data
// }
isLoading.dataListLoading = false
}
</script>
<template>
<Header :prep="{ ...headerPrep }" :ref-search-nav="refSearchNav" />
<AppPrescriptionList v-if="!isLoading.dataListLoading" />
<AppPrescriptionEntry />
<PrescriptionItemListEntry :data=[] />
<div>
<Button>
Tambah
</Button>
</div>
</template>
+29 -31
View File
@@ -202,39 +202,37 @@ watch(recId, () => {
</script> </script>
<template> <template>
<div class="rounded-md border p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppSpecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppSpecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFormEntryDialogOpen" :title="headerPrep.addNav?.label!" size="lg" prevent-outside> <Dialog v-model:open="isFormEntryDialogOpen" :title="headerPrep.addNav?.label!" size="lg" prevent-outside>
<AppSpecialistEntryForm <AppSpecialistEntryForm
:installation="installationConf" :installation="installationConf"
:unit="filteredUnitConf" :unit="filteredUnitConf"
:schema="schemaConf" :schema="schemaConf"
:disabled-unit="!selectedInstallationId" :disabled-unit="!selectedInstallationId"
:initial-values="{ name: '', code: '', installationId: '', unitId: '' }" :initial-values="{ name: '', code: '', installationId: '', unitId: '' }"
@submit="onSubmitForm" @submit="onSubmitForm"
@cancel="onCancelForm" @cancel="onCancelForm"
@installation-changed="onInstallationChanged" @installation-changed="onInstallationChanged"
/> />
</Dialog> </Dialog>
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="handleConfirmDelete" @confirm="handleConfirmDelete"
@cancel="handleCancelConfirmation" @cancel="handleCancelConfirmation"
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p> <p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
<style scoped> <style scoped>
+31 -33
View File
@@ -203,39 +203,37 @@ watch(recId, () => {
</script> </script>
<template> <template>
<div class="rounded-md border p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppSubspecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppSubspecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFormEntryDialogOpen" :title="headerPrep.addNav?.label!" size="lg" prevent-outside> <Dialog v-model:open="isFormEntryDialogOpen" :title="headerPrep.addNav?.label!" size="lg" prevent-outside>
<AppSubspecialistEntryForm <AppSubspecialistEntryForm
:installation="installationConf" :installation="installationConf"
:unit="filteredUnitConf" :unit="filteredUnitConf"
:specialist="specialistConf" :specialist="specialistConf"
:schema="schemaConf" :schema="schemaConf"
:initial-values="{ name: '', code: '', installationId: '', unitId: '', specialistId: '' }" :initial-values="{ name: '', code: '', installationId: '', unitId: '', specialistId: '' }"
@submit="onSubmitForm" @submit="onSubmitForm"
@cancel="onCancelForm" @cancel="onCancelForm"
@installation-changed="onInstallationChanged" @installation-changed="onInstallationChanged"
@unit-changed="onUnitChanged" @unit-changed="onUnitChanged"
/> />
</Dialog> </Dialog>
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="handleConfirmDelete" @confirm="handleConfirmDelete"
@cancel="handleCancelConfirmation" @cancel="handleCancelConfirmation"
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p> <p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p v-if="record?.specialist"><strong>Specialist:</strong> {{ record.specialist?.name }}</p> <p v-if="record?.specialist"><strong>Specialist:</strong> {{ record.specialist?.name }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
+41 -45
View File
@@ -131,52 +131,48 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppToolsList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<div class="rounded-md border p-4">
<AppToolsList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
<Dialog <Dialog
v-model:open="isFormEntryDialogOpen" v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Peralatan'" :title="!!recItem ? title : 'Tambah Peralatan'"
size="lg" size="lg"
prevent-outside prevent-outside
> >
<AppToolsEntryForm <AppToolsEntryForm
:schema="DeviceSchema" :schema="DeviceSchema"
:values="recItem" :values="recItem"
:uoms="uoms" :uoms="uoms"
:is-loading="isProcessing" :is-loading="isProcessing"
:is-readonly="isReadonly" :is-readonly="isReadonly"
@submit=" @submit="
(values: DeviceFormData, resetForm: any) => { (values: DeviceFormData, resetForm: any) => {
if (recId > 0) { if (recId > 0) {
handleActionEdit(recId, values, getToolsList, resetForm, toast) handleActionEdit(recId, values, getToolsList, resetForm, toast)
return return
}
handleActionSave(values, getToolsList, resetForm, toast)
} }
" handleActionSave(values, getToolsList, resetForm, toast)
@cancel="handleCancelForm" }
/> "
</Dialog> @cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" v-model:open="isRecordConfirmationOpen"
action="delete" action="delete"
:record="recItem" :record="recItem"
@confirm="() => handleActionRemove(recId, getToolsList, toast)" @confirm="() => handleActionRemove(recId, getToolsList, toast)"
@cancel="" @cancel=""
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p> <p v-if="record?.name"><strong>Nama:</strong> {{ record.name }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
+19 -21
View File
@@ -177,31 +177,29 @@ function handleCancelConfirmation() {
</script> </script>
<template> <template>
<div class="rounded-md border p-4"> <Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" />
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" /> <AppUnitList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppUnitList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Unit" size="lg" prevent-outside> <Dialog v-model:open="isFormEntryDialogOpen" title="Tambah Unit" size="lg" prevent-outside>
<AppUnitEntryForm <AppUnitEntryForm
:unit="unitConf" :schema="schemaConf" :initial-values="{ name: '', code: '', parentId: '' }" :unit="unitConf" :schema="schemaConf" :initial-values="{ name: '', code: '', parentId: '' }"
@submit="onSubmitForm" @cancel="onCancelForm" @submit="onSubmitForm" @cancel="onCancelForm"
/> />
</Dialog> </Dialog>
<!-- Record Confirmation Modal --> <!-- Record Confirmation Modal -->
<RecordConfirmation <RecordConfirmation
v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem" v-model:open="isRecordConfirmationOpen" action="delete" :record="recItem"
@confirm="handleConfirmDelete" @cancel="handleCancelConfirmation" @confirm="handleConfirmDelete" @cancel="handleCancelConfirmation"
> >
<template #default="{ record }"> <template #default="{ record }">
<div class="text-sm"> <div class="text-sm">
<p><strong>ID:</strong> {{ record?.id }}</p> <p><strong>ID:</strong> {{ record?.id }}</p>
<p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p> <p v-if="record?.firstName"><strong>Nama:</strong> {{ record.firstName }}</p>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p> <p v-if="record?.code"><strong>Kode:</strong> {{ record.cellphone }}</p>
</div> </div>
</template> </template>
</RecordConfirmation> </RecordConfirmation>
</div>
</template> </template>
<style scoped> <style scoped>
+3 -3
View File
@@ -19,10 +19,10 @@ const openCollapsible = ref(false)
<template> <template>
<SidebarMenu> <SidebarMenu>
<Collapsible :key="item.title" v-model:open="openCollapsible" as-child class="group/collapsible"> <Collapsible :key="item.title" v-model:open="openCollapsible" as-child class="group/collapsible ">
<SidebarMenuItem> <SidebarMenuItem>
<CollapsibleTrigger as-child> <CollapsibleTrigger as-child>
<SidebarMenuButton :tooltip="item.title" :size="size"> <SidebarMenuButton :tooltip="item.title" :size="size" class="md:!text-xs xl:!text-sm 2xl:!text-base">
<Icon :name="item.icon || ''" mode="svg" /> <Icon :name="item.icon || ''" mode="svg" />
<span class="mx-2">{{ item.title }}</span> <span class="mx-2">{{ item.title }}</span>
<Icon <Icon
@@ -37,7 +37,7 @@ const openCollapsible = ref(false)
<SidebarMenuSubButton as-child> <SidebarMenuSubButton as-child>
<NuxtLink <NuxtLink
:to="subItem.link" :to="subItem.link"
class="mx-4 rounded-lg py-5 text-sm transition-all duration-200" class="mx-4 rounded-lg py-5 text-sm transition-all duration-200 md:!text-xs xl:!text-sm 2xl:!text-base"
active-class="bg-primary text-white" active-class="bg-primary text-white"
@click="setOpenMobile(false)" @click="setOpenMobile(false)"
> >
+2 -2
View File
@@ -21,7 +21,7 @@ const { setOpenMobile } = useSidebar()
<SidebarMenuButton as-child :tooltip="item.title" :size="size" class=""> <SidebarMenuButton as-child :tooltip="item.title" :size="size" class="">
<NuxtLink <NuxtLink
:to="item.link" :to="item.link"
class="group flex items-center gap-3 rounded-lg px-2 py-4 text-sm transition-all duration-200" class="group flex items-center gap-3 rounded-lg px-2 py-4 text-sm transition-all duration-200 md:!text-xs xl:!text-sm 2xl:!text-base"
active-class="bg-primary text-white" active-class="bg-primary text-white"
@click="setOpenMobile(false)" @click="setOpenMobile(false)"
> >
@@ -29,7 +29,7 @@ const { setOpenMobile } = useSidebar()
<span class="mx-1">{{ item.title }}</span> <span class="mx-1">{{ item.title }}</span>
<span <span
v-if="item.new" v-if="item.new"
class="bg-#adfa1d rounded-md px-1.5 py-0.5 text-xs leading-none text-black no-underline group-hover:no-underline" class="bg-#adfa1d rounded-md px-1.5 py-0.5 text-xs leading-none text-black no-underline group-hover:no-underline "
> >
New New
</span> </span>
@@ -30,7 +30,7 @@ const getLabelSizeIdx = (size: string) => {
const settingClass = computed(() => { const settingClass = computed(() => {
const breakPointIdx = getBreakpointIdx(props.gridPoint) const breakPointIdx = getBreakpointIdx(props.gridPoint)
let cls = breakpoints[breakPointIdx] let cls = breakpoints[breakPointIdx]
cls += ' gap-4 xl:gap-5 ' + [ cls += ' gap-x-4 xl:gap-x-5 gap-y-2 xl:gap-y-3 ' + [
'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5', 'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5',
'grid-cols-6', 'grid-cols-7', 'grid-cols-8', 'grid-cols-9', 'grid-cols-10', 'grid-cols-6', 'grid-cols-7', 'grid-cols-8', 'grid-cols-9', 'grid-cols-10',
][props.colCount - 1] ][props.colCount - 1]
+2 -2
View File
@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
labelFor: string labelFor?: string
size?: 'default' | 'narrow' | 'wide' size?: 'default' | 'narrow' | 'wide'
height?: 'default' | 'compact' height?: 'default' | 'compact'
position?: 'default' | 'dynamic' position?: 'default' | 'dynamic'
@@ -48,7 +48,7 @@ const labelClass = computed(() => [props.stacked ? 'block mb-1 text-sm font-medi
<template> <template>
<div :class="wrapperClass"> <div :class="wrapperClass">
<label class="block text-base font-medium text-gray-600" :class="labelClass" :for="labelFor"> <label class="block" :class="labelClass" :for="labelFor">
<slot /> <slot />
</label> </label>
</div> </div>
@@ -23,21 +23,23 @@ function btnClick() {
<header> <header>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<div class="ml-3 text-lg font-bold text-gray-900"> <div class="text-lg font-semibold text-gray-900">
<Icon :name="props.prep.icon!" class="mr-2 size-4 align-middle md:size-6" /> <Icon :name="props.prep.icon!" class="mr-2 size-4 md:size-6 align-middle" />
{{ props.prep.title }} {{ props.prep.title }}
</div> </div>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<div v-if="props.refSearchNav" class="ml-3 text-lg text-gray-900"> <div v-if="props.refSearchNav" class="text-lg text-gray-900">
<!-- <Input --> <Input
<!-- type="text" placeholder="Search" --> type="text"
<!-- class="w-full rounded-md border bg-white px-4 py-2 text-gray-900 sm:text-sm" @click="emitSearchNavClick" --> placeholder="Search"
<!-- @input="onInput" --> class="sm:text-sm"
<!-- /> --> @click="emitSearchNavClick"
@input="onInput"
/>
</div> </div>
<div v-if="prep.addNav" class="m-2 flex items-center"> <div v-if="prep.addNav" class="flex items-center ms-2">
<Button size="md" class="rounded-md border border-gray-300 px-4 py-2 text-white sm:text-sm" @click="btnClick"> <Button class="rounded-md border border-gray-300 px-4 py-2 text-white sm:text-sm" @click="btnClick">
<Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle" /> <Icon name="i-lucide-plus" class="mr-2 h-4 w-4 align-middle" />
{{ prep.addNav.label }} {{ prep.addNav.label }}
</Button> </Button>
+1 -1
View File
@@ -24,7 +24,7 @@ const modelValue = useVModel(props, 'modelValue', emits, {
v-model="modelValue" v-model="modelValue"
:class=" :class="
cn( cn(
'border-input ring-offset-background placeholder:text-muted-foreground flex h-9 w-full rounded-md border border-gray-400 px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50', 'border-input dark:bg-slate-950 ring-offset-background placeholder:text-muted-foreground flex h-8 xl:h-9 w-full rounded-md border border-gray-400 px-3 py-2 md:text-xs xl:text-sm file:border-0 file:bg-transparent md:file:text-xs xl:file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50',
props.class, props.class,
) )
" "
+1 -1
View File
@@ -19,7 +19,7 @@ const delegatedProps = computed(() => {
v-bind="delegatedProps" v-bind="delegatedProps"
:class=" :class="
cn( cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', 'md:!text-xs xl:text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class, props.class,
) )
" "
@@ -21,7 +21,7 @@ const iconName = computed(() => props.iconName || 'i-radix-icons-caret-sort')
v-bind="forwardedProps" v-bind="forwardedProps"
:class=" :class="
cn( cn(
'border-input ring-offset-background placeholder:text-muted-foreground relative flex h-10 w-full rounded-md border border-gray-400 pl-3 pr-8 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50', 'border-input ring-offset-background placeholder:text-muted-foreground relative flex h-9 md:h-8 xl:h-9 w-full rounded-md border border-gray-400 pl-3 pr-8 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50',
props.class, props.class,
) )
" "
+26 -90
View File
@@ -3,18 +3,22 @@ import CardContent from '~/components/pub/ui/card/CardContent.vue'
const route = useRoute() const route = useRoute()
const contentFrame = computed(() => route.meta.contentFrame) const contentFrame = computed(() => route.meta.contentFrame)
const contentContent = computed(() => { const contentFrameClass = computed(() => {
switch (contentFrame.value) { switch (contentFrame.value) {
case 'cf-container-lg': case 'cf-container-2xl':
return 'cf-frame cf-container-lg-content' return 'cf-container-2xl'
case 'cf-container-2xl':
return 'cf-container-xl'
case 'cf-container-xl':
return 'cf-container-lg'
case 'cf-container-md': case 'cf-container-md':
return 'cf-frame cf-container-md-content' return 'cf-container-md'
case 'cf-container-sm': case 'cf-container-sm':
return 'cf-frame cf-container-sm-content' return 'cf-container-sm'
case 'cf-full-width': case 'cf-full-width':
return 'cf-frame-width' return 'cf-full-width'
default: default:
return 'cf-frame' return 'cf-container-lg'
} }
}) })
</script> </script>
@@ -24,8 +28,8 @@ const contentContent = computed(() => {
<LayoutAppSidebar /> <LayoutAppSidebar />
<SidebarInset> <SidebarInset>
<LayoutHeader /> <LayoutHeader />
<div class="w-full min-w-0 flex-1 overflow-x-auto p-4 xl:p-5 2xl:p-6"> <div :class="`w-full p-4 xl:p-5 2xl:p-6 flex justify-center ${contentFrameClass}`">
<div v-if="contentFrame !== 'cf-no-frame'" :class="`contentFrame ${contentContent} ${contentFrame}`"> <div v-if="contentFrame !== 'cf-no-frame'">
<Card> <Card>
<CardContent> <CardContent>
<slot /> <slot />
@@ -39,34 +43,24 @@ const contentContent = computed(() => {
</template> </template>
<style scoped> <style scoped>
.cf-container,
.cf-container-lg,
.cf-container-md,
.cf-container-sm {
/* container-type: inline-size;
max-width: 100%;
margin-left: auto;
margin-right: auto;
border-radius: 0.375rem;
padding-bottom: 5rem; */
/* padding-left: 1rem;
padding-right: 1rem; */
}
.cf-container > *, .cf-container > *,
.cf-container-lg > *, .cf-container-lg > *,
.cf-container-md > *, .cf-container-md > *,
.cf-container-sm > *, .cf-container-sm > *,
.cf-full-width { .cf-full-width {
/* margin-left: auto; width: 100%;
margin-right: auto; */ }
/* padding: 0.75rem;
padding-bottom: 5rem; */ .cf-full-width > * {
/* background-color: hsl(var(--background)); width: 100%;
border-radius: 0.375rem; }
border: 1px solid hsl(var(--border));
border-color: rgb(226 232 240); .cf-container-2xl > * {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); */ max-width: 1400px;
}
.cf-container-xl > * {
max-width: 1200px;
} }
.cf-container-lg > * { .cf-container-lg > * {
@@ -80,62 +74,4 @@ const contentContent = computed(() => {
.cf-container-sm > * { .cf-container-sm > * {
max-width: 576px; max-width: 576px;
} }
.cf-frame-width {
/* margin-left: auto;
margin-right: auto;
background-color: hsl(var(--background));
border-radius: 0.375rem;
border: 1px solid hsl(var(--border));
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-width: 100%; */
/* padding: 1rem; */
}
.cf-frame {
/* margin-left: auto;
margin-right: auto;
padding: 1rem;
background-color: hsl(var(--background));
border-radius: 0.375rem;
border: 1px solid hsl(var(--border));
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-width: 100%; */
}
@media (min-width: 640px) {
.cf-container,
.cf-container-lg,
.cf-container-md,
.cf-container-sm {
/* padding-left: 2rem;
padding-right: 2rem; */
}
.cf-frame {
/* padding: 2rem; */
}
.cf-frame-width {
/* padding: 2rem; */
}
}
@media (min-width: 1024px) {
.cf-container,
.cf-container-lg,
.cf-container-md,
.cf-container-sm {
/* padding-left: 3rem;
padding-right: 3rem; */
}
.cf-frame {
/* padding: 3rem; */
}
.cf-frame-width {
/* padding: 3rem; */
}
}
</style> </style>
@@ -32,10 +32,8 @@ const canRead = true
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentDivisionList />
<ContentDivisionList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -32,10 +32,8 @@ const canRead = true
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentInstallationList />
<ContentInstallationList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -32,10 +32,8 @@ const canRead = true
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentSpecialistList />
<ContentSpecialistList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -32,10 +32,8 @@ const canRead = true
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentSubspecialistList />
<ContentSubspecialistList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
+4 -6
View File
@@ -32,10 +32,8 @@ const canRead = true
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentUnitList />
<ContentUnitList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -7,7 +7,7 @@ definePageMeta({
middleware: ['rbac'], middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'], roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Daftar User', title: 'Daftar User',
contentFrame: 'cf-full-width', contentFrame: 'cf-container-lg',
}) })
const route = useRoute() const route = useRoute()
@@ -31,10 +31,8 @@ const canRead = true // hasReadAccess(roleAccess)
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentEquipmentList />
<ContentEquipmentList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -7,7 +7,7 @@ definePageMeta({
middleware: ['rbac'], middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'], roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Daftar Dokter', title: 'Daftar Dokter',
contentFrame: 'cf-full-width', contentFrame: 'cf-container-lg',
}) })
const route = useRoute() const route = useRoute()
@@ -31,10 +31,8 @@ const canRead = hasReadAccess(roleAccess)
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentMedicineMethodList />
<ContentMedicineMethodList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -7,7 +7,7 @@ definePageMeta({
middleware: ['rbac'], middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'], roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Daftar Dokter', title: 'Daftar Dokter',
contentFrame: 'cf-full-width', contentFrame: 'cf-container-lg',
}) })
const route = useRoute() const route = useRoute()
@@ -31,10 +31,8 @@ const canRead = hasReadAccess(roleAccess)
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentMedicineGroupList />
<ContentMedicineGroupList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -34,8 +34,8 @@ const canCreate = hasCreateAccess(roleAccess)
</script> </script>
<template> <template>
<div v-if="canCreate"> <template v-if="canCreate">
<ContentMedicineEntry /> <ContentMedicineEntry />
</div> </template>
<Error v-else :status-code="403" /> <Error v-else :status-code="403" />
</template> </template>
@@ -31,10 +31,8 @@ const canRead = hasReadAccess(roleAccess)
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentMedicineList />
<ContentMedicineList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
@@ -7,7 +7,7 @@ definePageMeta({
middleware: ['rbac'], middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'], roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Daftar User', title: 'Daftar User',
contentFrame: 'cf-full-width', contentFrame: 'cf-container-lg',
}) })
const route = useRoute() const route = useRoute()
@@ -31,10 +31,8 @@ const canRead = true // hasReadAccess(roleAccess)
</script> </script>
<template> <template>
<div> <template v-if="canRead">
<div v-if="canRead"> <ContentToolsList />
<ContentToolsList /> </template>
</div> <Error v-else :status-code="403" />
<Error v-else :status-code="403" />
</div>
</template> </template>
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
// import type { PagePermission } from '~/models/role'
import Error from '~/components/pub/base/error/error.vue'
// import { PAGE_PERMISSIONS } from '~/lib/page-permission'
definePageMeta({
// middleware: ['rbac'],
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
title: 'Daftar Divisi',
contentFrame: 'cf-container-lg',
})
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">
<ContentPrescriptionList />
</div>
<Error v-else :status-code="403" />
</div>
</template>
+1
View File
@@ -42,6 +42,7 @@ export default withNuxt(
// Allow more flexible code style // Allow more flexible code style
'style/max-statements-per-line': ['error', { max: 3 }], 'style/max-statements-per-line': ['error', { max: 3 }],
'antfu/if-newline': 'off', // Disable newline after if requirement 'antfu/if-newline': 'off', // Disable newline after if requirement
'antfu/top-level-function': false,
}, },
}, },
{ {
+116
View File
@@ -0,0 +1,116 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
"children": [
{
"title": "Triase",
"icon": "i-lucide-stethoscope",
"link": "/outpatient/encounter"
},
{
"title": "Konsultasi",
"icon": "i-lucide-building-2",
"link": "/outpatient/consultation"
}
]
},
{
"title": "IGD",
"icon": "i-lucide-zap",
"children": [
{
"title": "Triase",
"icon": "i-lucide-stethoscope",
"link": "/emergency/triage"
},
{
"title": "Kunjungan",
"icon": "i-lucide-building-2",
"link": "/emergency/encounter"
},
{
"title": "Konsultasi",
"icon": "i-lucide-building-2",
"link": "/emergency/consultation"
}
]
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"children": [
{
"title": "Kunjungan",
"icon": "i-lucide-building-2",
"link": "/rehab/encounter"
},
{
"title": "Konsultasi",
"icon": "i-lucide-building-2",
"link": "/rehab/consultation"
}
]
},
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
"children": [
{
"title": "Kunjungan",
"icon": "i-lucide-building-2",
"link": "/inpatient/encounter"
},
{
"title": "Konsultasi",
"icon": "i-lucide-building-2",
"link": "/inpatient/consultation"
}
]
}
]
},
{
"heading": "Ruang Tindakan Rajal",
"items": [
{
"title": "Kemoterapi",
"icon": "i-lucide-droplets",
"link": "/cemotherapy/encounter"
},
{
"title": "Hemofilia",
"icon": "i-lucide-droplet-off",
"link": "/hemophilia/encounter"
}
]
},
{
"heading": "Ruang Tindakan Anak",
"items": [
{
"title": "Thalasemi",
"icon": "i-lucide-baby",
"link": "/thalasemia/encounter"
},
{
"title": "Echocardiography",
"icon": "i-lucide-baby",
"link": "/echocardiography/encounter"
},
{
"title": "Spirometri",
"icon": "i-lucide-baby",
"link": "/spirometry/encounter"
}
]
}
]
+58
View File
@@ -0,0 +1,58 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Lab - Order",
"icon": "i-lucide-microscope",
"link": "/lab-order"
},
{
"title": "Lab Mikro - Order",
"icon": "i-lucide-microscope",
"link": "/micro-lab-order"
},
{
"title": "Lab PA - Order",
"icon": "i-lucide-microscope",
"link": "/pa-lab-order"
},
{
"title": "Radiologi - Order",
"icon": "i-lucide-radio",
"link": "/radiology-order"
},
{
"title": "Gizi",
"icon": "i-lucide-egg-fried",
"link": "/nutrition-order"
},
{
"title": "Pembayaran",
"icon": "i-lucide-banknote-arrow-up",
"link": "/payment"
}
]
},
{
"heading": "Source",
"items": [
{
"title": "Umum",
"icon": "i-lucide-airplay",
"children": [
{
"title": "Uom",
"icon": "i-lucide-airplay",
"link": "/common/uom"
}
]
}
]
}
]
+32
View File
@@ -0,0 +1,32 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
"link": "/outpatient/encounter"
},
{
"title": "IGD",
"icon": "i-lucide-zap",
"link": "/emergency/encounter"
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"link": "/rehab/polyclinic-queue"
},
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
"link": "/inpatient/encounter"
}
]
}
]
+95
View File
@@ -0,0 +1,95 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
"children": [
{
"title": "Antrian Poliklinik",
"link": "/outpatient/polyclinic-queue"
},
{
"title": "Pendaftaran",
"link": "/outpatient/encounter"
}
]
},
{
"title": "IGD",
"icon": "i-lucide-zap",
"children": [
{
"title": "Triase",
"link": "/emergency/triage"
},
{
"title": "Pemeriksaan",
"link": "/emergency/encounter"
}
]
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"link": "/rehab",
"children": [
{
"title": "Antrean Poliklinik",
"link": "/rehab/polyclinic-queue"
},
{
"title": "Kunjungan",
"link": "/rehab/encounter"
}
]
},
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
"link": "/inpatient/encounter"
}
]
},
{
"heading": "Ruang Tindakan Rajal",
"items": [
{
"title": "Kemoterapi",
"icon": "i-lucide-droplets",
"link": "/cemotherapy/encounter"
},
{
"title": "Hemofilia",
"icon": "i-lucide-droplet-off",
"link": "/hemophilia/encounter"
}
]
},
{
"heading": "Ruang Tindakan Anak",
"items": [
{
"title": "Thalasemi",
"icon": "i-lucide-baby",
"link": "/thalasemia/encounter"
},
{
"title": "Echocardiography",
"icon": "i-lucide-baby",
"link": "/echocardiography/encounter"
},
{
"title": "Spirometri",
"icon": "i-lucide-baby",
"link": "/spirometry/encounter"
}
]
}
]
+67
View File
@@ -0,0 +1,67 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
"link": "/outpatient/encounter"
},
{
"title": "IGD",
"icon": "i-lucide-zap",
"link": "/emergency/encounter"
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"link": "/rehab/encounter"
},
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
"link": "/inpatient/encounter"
}
]
},
{
"heading": "Ruang Tindakan Rajal",
"items": [
{
"title": "Kemoterapi",
"icon": "i-lucide-droplets",
"link": "/outpatient-medical-act/cemotherapy"
},
{
"title": "Hemofilia",
"icon": "i-lucide-droplet-off",
"link": "/outpatient-medical-act/hemophilia"
}
]
},
{
"heading": "Ruang Tindakan Anak",
"items": [
{
"title": "Thalasemi",
"icon": "i-lucide-baby",
"link": "/child-medical-act/thalasemia"
},
{
"title": "Echocardiography",
"icon": "i-lucide-baby",
"link": "/child-medical-act/echocardiography"
},
{
"title": "Spirometri",
"icon": "i-lucide-baby",
"link": "/child-medical-act/spirometry"
}
]
}
]
+75
View File
@@ -0,0 +1,75 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Obat - Order",
"icon": "i-lucide-briefcase-medical",
"children": [
{
"title": "Permintaan",
"icon": "i-lucide-user",
"link": "/medication/order"
},
{
"title": "Standing Order",
"icon": "i-lucide-user",
"link": "/medication/standing-order"
}
]
}
]
},
{
"heading": "Source",
"items": [
{
"title": "Peralatan dan Perlengkapan",
"icon": "i-lucide-layout-dashboard",
"children": [
{
"title": "Obat",
"icon": "i-lucide-stethoscope",
"link": "/tools-equipment-src/medicine"
},
{
"title": "Peralatan",
"icon": "i-lucide-tools",
"link": "/tools-equipment-src/tools"
},
{
"title": "Perlengkapan (BMHP)",
"icon": "i-lucide-stethoscope",
"link": "/tools-equipment-src/equipment"
},
{
"title": "Metode Obat",
"icon": "i-lucide-user",
"link": "/tools-equipment-src/medicine-method"
},
{
"title": "Jenis Obat",
"icon": "i-lucide-user",
"link": "/tools-equipment-src/medicine-type"
}
]
},
{
"title": "Umum",
"icon": "i-lucide-airplay",
"children": [
{
"title": "Uom",
"icon": "i-lucide-airplay",
"link": "/common/uom"
}
]
}
]
}
]
+75
View File
@@ -0,0 +1,75 @@
[
{
"heading": "Menu Utama",
"items": [
{
"title": "Dashboard",
"icon": "i-lucide-home",
"link": "/"
},
{
"title": "Rawat Jalan",
"icon": "i-lucide-stethoscope",
"children": [
{
"title": "Antrian Pendaftaran",
"link": "/outpatient/registration-queue"
},
{
"title": "Kunjungan",
"link": "/outpatient/encounter"
}
]
},
{
"title": "IGD",
"icon": "i-lucide-zap",
"link": "/emergency/encounter"
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"link": "/rehab",
"children": [
{
"title": "Antrean Pendaftaran",
"link": "/rehab/registration-queue"
},
{
"title": "Kunjungan",
"link": "/rehab/encounter"
}
]
},
{
"title": "Rawat Inap",
"icon": "i-lucide-building-2",
"children": [
{
"title": "Permintaan",
"link": "/inpatient/request"
},
{
"title": "Kunjungan",
"link": "/inpatient/encounter"
}
]
}
]
},
{
"heading": "Client",
"items": [
{
"title": "Pasien",
"icon": "i-lucide-users",
"link": "/client/patient"
},
{
"title": "Rekam Medis",
"icon": "i-lucide-file-text",
"link": "/client/medical-record"
}
]
}
]
+98 -80
View File
@@ -13,23 +13,19 @@
"children": [ "children": [
{ {
"title": "Antrian Pendaftaran", "title": "Antrian Pendaftaran",
"icon": "i-lucide-stethoscope",
"link": "/outpatient/registration-queue" "link": "/outpatient/registration-queue"
}, },
{ {
"title": "Pendaftaran", "title": "Antrian Poliklinik",
"icon": "i-lucide-building-2", "link": "/outpatient/polyclinic-queue"
"link": "/outpatient/registration"
}, },
{ {
"title": "Antrian Pemeriksaan", "title": "Kunjungan",
"icon": "i-lucide-stethoscope", "link": "/outpatient/encounter"
"link": "/outpatient/examination-queue"
}, },
{ {
"title": "Pendaftaran", "title": "Konsultasi",
"icon": "i-lucide-building-2", "link": "/outpatient/consultation"
"link": "/outpatient/examination"
} }
] ]
}, },
@@ -39,18 +35,37 @@
"children": [ "children": [
{ {
"title": "Triase", "title": "Triase",
"icon": "i-lucide-stethoscope",
"link": "/emergency/triage" "link": "/emergency/triage"
}, },
{ {
"title": "Pendaftaran", "title": "Kunjungan",
"icon": "i-lucide-building-2", "link": "/emergency/encounter"
"link": "/emergency/registration"
}, },
{ {
"title": "Pemeriksaan", "title": "Konsultasi",
"icon": "i-lucide-building-2", "link": "/emergency/consultation"
"link": "/emergency/examination" }
]
},
{
"title": "Rehab Medik",
"icon": "i-lucide-bike",
"children": [
{
"title": "Antrean Pendaftaran",
"link": "/rehab/registration-queue"
},
{
"title": "Antrean Poliklinik",
"link": "/rehab/polyclinic-queue"
},
{
"title": "Kunjungan",
"link": "/rehab/encounter"
},
{
"title": "Konsultasi",
"link": "/rehab/consultation"
} }
] ]
}, },
@@ -60,45 +75,15 @@
"children": [ "children": [
{ {
"title": "Permintaan", "title": "Permintaan",
"icon": "i-lucide-stethoscope", "link": "/inpatient/request"
"link": "/outpatient/request"
},
{
"title": "Pendaftaran",
"icon": "i-lucide-building-2",
"link": "/outpatient/registration"
},
{
"title": "Pemeriksaan",
"icon": "i-lucide-building-2",
"link": "/outpatient/examination"
}
]
},
{
"title": "Rehabilitasi Medik",
"icon": "i-lucide-bike",
"link": "/rehabilitasi",
"children": [
{
"title": "Antrian Poliklinik",
"icon": "i-lucide-stethoscope",
"link": "/rehab/examination-queue"
},
{
"title": "Pendaftaran",
"icon": "i-lucide-building-2",
"link": "/rehab/registration"
},
{
"title": "Antrian Pemeriksaan",
"icon": "i-lucide-stethoscope",
"link": "/rehab/examination-queue"
}, },
{ {
"title": "Kunjungan", "title": "Kunjungan",
"icon": "i-lucide-building-2", "link": "/inpatient/encounter"
"link": "/rehab/encounter" },
{
"title": "Konsultasi",
"link": "/inpatient/consultation"
} }
] ]
}, },
@@ -108,12 +93,10 @@
"children": [ "children": [
{ {
"title": "Permintaan", "title": "Permintaan",
"icon": "i-lucide-user",
"link": "/medication/order" "link": "/medication/order"
}, },
{ {
"title": "Standing Order", "title": "Standing Order",
"icon": "i-lucide-user",
"link": "/medication/standing-order" "link": "/medication/standing-order"
} }
] ]
@@ -150,6 +133,41 @@
} }
] ]
}, },
{
"heading": "Ruang Tindakan Rajal",
"items": [
{
"title": "Kemoterapi",
"icon": "i-lucide-droplets",
"link": "/cemotherapy/encounter"
},
{
"title": "Hemofilia",
"icon": "i-lucide-droplet-off",
"link": "/hemophilia/encounter"
}
]
},
{
"heading": "Ruang Tindakan Anak",
"items": [
{
"title": "Thalasemi",
"icon": "i-lucide-baby",
"link": "/thalasemia/encounter"
},
{
"title": "Echocardiography",
"icon": "i-lucide-baby",
"link": "/echocardiography/encounter"
},
{
"title": "Spirometri",
"icon": "i-lucide-baby",
"link": "/spirometry/encounter"
}
]
},
{ {
"heading": "Client", "heading": "Client",
"items": [ "items": [
@@ -210,27 +228,22 @@
"children": [ "children": [
{ {
"title": "Obat", "title": "Obat",
"icon": "i-lucide-stethoscope",
"link": "/tools-equipment-src/medicine" "link": "/tools-equipment-src/medicine"
}, },
{ {
"title": "Peralatan", "title": "Peralatan",
"icon": "i-lucide-tools",
"link": "/tools-equipment-src/tools" "link": "/tools-equipment-src/tools"
}, },
{ {
"title": "Perlengkapan (BMHP)", "title": "Perlengkapan (BMHP)",
"icon": "i-lucide-stethoscope",
"link": "/tools-equipment-src/equipment" "link": "/tools-equipment-src/equipment"
}, },
{ {
"title": "Metode Obat", "title": "Metode Obat",
"icon": "i-lucide-user",
"link": "/tools-equipment-src/medicine-method" "link": "/tools-equipment-src/medicine-method"
}, },
{ {
"title": "Jenis Obat", "title": "Jenis Obat",
"icon": "i-lucide-user",
"link": "/tools-equipment-src/medicine-type" "link": "/tools-equipment-src/medicine-type"
} }
] ]
@@ -241,48 +254,54 @@
"children": [ "children": [
{ {
"title": "Pegawai", "title": "Pegawai",
"icon": "i-lucide-stethoscope",
"link": "/human-src/employee" "link": "/human-src/employee"
}, },
{ {
"title": "PPDS", "title": "PPDS",
"icon": "i-lucide-user",
"link": "/human-src/specialist-intern" "link": "/human-src/specialist-intern"
} }
] ]
}, },
{
"title": "Pemeriksaan Penunjang",
"icon": "i-lucide-layout-list",
"children": [
{
"title": "Checkup",
"link": "/mcu-src/mcu"
}
]
},
{ {
"title": "Layanan", "title": "Layanan",
"icon": "i-lucide-layout-list", "icon": "i-lucide-layout-list",
"children": [ "children": [
{ {
"title": "Counter", "title": "Counter",
"icon": "i-lucide-stethoscope",
"link": "/service-src/counter" "link": "/service-src/counter"
}, },
{ {
"title": "Public Screen (Big Screen)", "title": "Public Screen (Big Screen)",
"icon": "i-lucide-tools",
"link": "/service-src/public-screen" "link": "/service-src/public-screen"
}, },
{ {
"title": "Kasur", "title": "Kasur",
"icon": "i-lucide-tools",
"link": "/service-src/bed" "link": "/service-src/bed"
}, },
{ {
"title": "Kamar", "title": "Kamar",
"icon": "i-lucide-stethoscope", "link": "/service-src/chamber"
},
{
"title": "Ruang",
"link": "/service-src/chamber" "link": "/service-src/chamber"
}, },
{ {
"title": "Lantai", "title": "Lantai",
"icon": "i-lucide-user",
"link": "/service-src/floor" "link": "/service-src/floor"
}, },
{ {
"title": "Gedung", "title": "Gedung",
"icon": "i-lucide-user",
"link": "/service-src/building" "link": "/service-src/building"
} }
] ]
@@ -293,27 +312,22 @@
"children": [ "children": [
{ {
"title": "Divisi", "title": "Divisi",
"icon": "i-lucide-stethoscope",
"link": "/org-src/division" "link": "/org-src/division"
}, },
{ {
"title": "Instalasi", "title": "Instalasi",
"icon": "i-lucide-tools",
"link": "/org-src/installation" "link": "/org-src/installation"
}, },
{ {
"title": "Unit", "title": "Unit",
"icon": "i-lucide-tools",
"link": "/org-src/unit" "link": "/org-src/unit"
}, },
{ {
"title": "Specialist", "title": "Specialist",
"icon": "i-lucide-stethoscope",
"link": "/org-src/specialist" "link": "/org-src/specialist"
}, },
{ {
"title": "Sub Specialist", "title": "Sub Specialist",
"icon": "i-lucide-user",
"link": "/org-src/subspecialist" "link": "/org-src/subspecialist"
} }
] ]
@@ -322,18 +336,22 @@
"title": "Umum", "title": "Umum",
"icon": "i-lucide-airplay", "icon": "i-lucide-airplay",
"children": [ "children": [
{
"title": "Item & Pricing",
"icon": "i-lucide-airplay",
"link": "/common/item"
},
{ {
"title": "Uom", "title": "Uom",
"icon": "i-lucide-airplay",
"link": "/common/uom" "link": "/common/uom"
} }
] ]
},
{
"title": "Keuangan",
"icon": "i-lucide-airplay",
"children": [
{
"title": "Item & Pricing",
"link": "/common/item"
}
]
} }
] ]
} }
] ]