Merge pull request #32 from dikstub-rssa/feat/equipment-device
Feat Equipment Device
This commit is contained in:
No files matched your search
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/custom-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/custom-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/custom-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/custom-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any; errors: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => {
|
||||||
|
emit('update:modelValue', val)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ value: 'item1', label: 'Item 1' },
|
||||||
|
{ value: 'item2', label: 'Item 2' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Kode</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.code" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup v-if="!!props.errors.code">
|
||||||
|
<Label></Label>
|
||||||
|
<span class="text-red-400 text-sm">{{ props.errors.code }}</span>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup v-if="!!props.errors.name">
|
||||||
|
<Label></Label>
|
||||||
|
<span class="text-red-400 text-sm">{{ props.errors.name }}</span>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Item</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih item" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Satuan</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.uom" :items="items" placeholder="Pilih item" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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: 100 }, { width: 250 }, { width: 100 }, { width: 100 }, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Item' }, { label: 'Satuan' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'item_id', 'uom_code', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: Record<string, (row: any, ...args: any[]) => any> = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.name}`.trim()
|
||||||
|
},
|
||||||
|
item_id: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.item_id
|
||||||
|
},
|
||||||
|
uom_code: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.uom_code
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: Record<string, (row: any, ...args: any[]) => any> = {}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubBaseDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Block from '~/components/pub/custom-ui/form/block.vue'
|
||||||
|
import FieldGroup from '~/components/pub/custom-ui/form/field-group.vue'
|
||||||
|
import Field from '~/components/pub/custom-ui/form/field.vue'
|
||||||
|
import Label from '~/components/pub/custom-ui/form/label.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: any; errors: any }>()
|
||||||
|
const emit = defineEmits(['update:modelValue', 'event'])
|
||||||
|
|
||||||
|
const data = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => {
|
||||||
|
emit('update:modelValue', val)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ value: 'item1', label: 'Item 1' },
|
||||||
|
{ value: 'item2', label: 'Item 2' },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form id="entry-form">
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<div class="flex flex-col justify-between">
|
||||||
|
<Block>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Kode</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.code" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup v-if="!!props.errors.code">
|
||||||
|
<Label></Label>
|
||||||
|
<span class="text-red-400 text-sm">{{ props.errors.code }}</span>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Nama</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.name" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup v-if="!!props.errors.name">
|
||||||
|
<Label></Label>
|
||||||
|
<span class="text-red-400 text-sm">{{ props.errors.name }}</span>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Item</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.type" :items="items" placeholder="Pilih item" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Satuan</Label>
|
||||||
|
<Field>
|
||||||
|
<Select v-model="data.uom" :items="items" placeholder="Pilih item" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
<FieldGroup :column="1">
|
||||||
|
<Label>Stok</Label>
|
||||||
|
<Field>
|
||||||
|
<Input v-model="data.stock" type="number" />
|
||||||
|
</Field>
|
||||||
|
</FieldGroup>
|
||||||
|
</Block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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: 100 }, { width: 250 }, { width: 100 }, { width: 100 }, { width: 50 }]
|
||||||
|
|
||||||
|
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Item' }, { label: 'Satuan' }]]
|
||||||
|
|
||||||
|
export const keys = ['code', 'name', 'item_id', 'uom_code', 'action']
|
||||||
|
|
||||||
|
export const delKeyNames: KeyLabel[] = [
|
||||||
|
{ key: 'code', label: 'Kode' },
|
||||||
|
{ key: 'name', label: 'Nama' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const funcParsed: Record<string, (row: any, ...args: any[]) => any> = {
|
||||||
|
name: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return `${recX.name}`.trim()
|
||||||
|
},
|
||||||
|
item_id: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.item_id
|
||||||
|
},
|
||||||
|
uom_code: (rec: unknown): unknown => {
|
||||||
|
const recX = rec as SmallDetailDto
|
||||||
|
return recX.uom_code
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcComponent: RecStrFuncComponent = {
|
||||||
|
action(rec, idx) {
|
||||||
|
const res: RecComponent = {
|
||||||
|
idx,
|
||||||
|
rec: rec as object,
|
||||||
|
component: action,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const funcHtml: Record<string, (row: any, ...args: any[]) => any> = {}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
data: any[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PubBaseDataTable
|
||||||
|
:rows="data"
|
||||||
|
:cols="cols"
|
||||||
|
:header="header"
|
||||||
|
:keys="keys"
|
||||||
|
:func-parsed="funcParsed"
|
||||||
|
:func-html="funcHtml"
|
||||||
|
:func-component="funcComponent"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Action from '~/components/pub/custom-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
import { z, ZodError } from 'zod'
|
||||||
|
|
||||||
|
const errors = ref({})
|
||||||
|
const data = ref({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
type: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
code: z.string().min(1, 'Code must be at least 1 characters long'),
|
||||||
|
name: z.string().min(1, 'Name must be at least 1 characters long'),
|
||||||
|
type: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onClick(type: string) {
|
||||||
|
if (type === 'cancel') {
|
||||||
|
navigateTo('/tools-equipment-src/device')
|
||||||
|
} else if (type === 'draft') {
|
||||||
|
// do something
|
||||||
|
} else if (type === 'submit') {
|
||||||
|
// do something
|
||||||
|
const input = data.value
|
||||||
|
console.log(input)
|
||||||
|
const errorsParsed: any = {}
|
||||||
|
try {
|
||||||
|
const result = schema.safeParse(input)
|
||||||
|
if (!result.success) {
|
||||||
|
// You can handle the error here, e.g. show a message
|
||||||
|
const errorsCaptures = result?.error?.errors || []
|
||||||
|
const errorMessage = result.error.errors[0]?.message ?? 'Validation error occurred'
|
||||||
|
errorsCaptures.forEach((value: any) => {
|
||||||
|
const keyName = value?.path?.length > 0 ? value.path[0] : 'key'
|
||||||
|
errorsParsed[keyName as string] = value.message || ''
|
||||||
|
})
|
||||||
|
console.log(errorMessage)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ZodError) {
|
||||||
|
const jsonError = e.flatten()
|
||||||
|
console.log(JSON.stringify(jsonError, null, 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
errors.value = errorsParsed
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Icon name="i-lucide-paint-bucket" class="me-2" />
|
||||||
|
<span class="font-semibold">Tambah</span> Alat Kesehatan
|
||||||
|
</div>
|
||||||
|
<AppDeviceEntryForm v-model="data" :errors="errors" />
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<Action @click="onClick" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<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'
|
||||||
|
|
||||||
|
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: 'Alat Kesehatan',
|
||||||
|
icon: 'i-lucide-paint-bucket',
|
||||||
|
addNav: {
|
||||||
|
label: 'Tambah',
|
||||||
|
onClick: () => navigateTo('/tools-equipment-src/device/add'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMaterialList() {
|
||||||
|
isLoading.dataListLoading = true
|
||||||
|
|
||||||
|
const resp = await xfetch('/api/v1/device')
|
||||||
|
if (resp.success) {
|
||||||
|
data.value = (resp.body as Record<string, any>).data
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.dataListLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMaterialList()
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rounded-md border p-4">
|
||||||
|
<Header :prep="{ ...headerPrep }" :ref-search-nav="refSearchNav" />
|
||||||
|
<div class="rounded-md border p-4">
|
||||||
|
<AppMaterialList v-if="!isLoading.dataListLoading" :data="data" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Action from '~/components/pub/custom-ui/nav-footer/ba-dr-su.vue'
|
||||||
|
import { z, ZodError } from 'zod'
|
||||||
|
|
||||||
|
const errors = ref({})
|
||||||
|
const data = ref({
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
type: '',
|
||||||
|
stock: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
code: z.string().min(1, 'Code must be at least 1 characters long'),
|
||||||
|
name: z.string().min(1, 'Name must be at least 1 characters long'),
|
||||||
|
type: z.string(),
|
||||||
|
stock: z.preprocess((val) => Number(val), z.number({ invalid_type_error: 'Stok harus berupa angka' })),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onClick(type: string) {
|
||||||
|
if (type === 'cancel') {
|
||||||
|
navigateTo('/tools-equipment-src/material')
|
||||||
|
} else if (type === 'draft') {
|
||||||
|
// do something
|
||||||
|
} else if (type === 'submit') {
|
||||||
|
// do something
|
||||||
|
const input = data.value
|
||||||
|
console.log(input)
|
||||||
|
const errorsParsed: any = {}
|
||||||
|
try {
|
||||||
|
const result = schema.safeParse(input)
|
||||||
|
if (!result.success) {
|
||||||
|
// You can handle the error here, e.g. show a message
|
||||||
|
const errorsCaptures = result?.error?.errors || []
|
||||||
|
const errorMessage = result.error.errors[0]?.message ?? 'Validation error occurred'
|
||||||
|
errorsCaptures.forEach((value: any) => {
|
||||||
|
const keyName = value?.path?.length > 0 ? value.path[0] : 'key'
|
||||||
|
errorsParsed[keyName as string] = value.message || ''
|
||||||
|
})
|
||||||
|
console.log(errorMessage)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ZodError) {
|
||||||
|
const jsonError = e.flatten()
|
||||||
|
console.log(JSON.stringify(jsonError, null, 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
errors.value = errorsParsed
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
|
||||||
|
<Icon name="i-lucide-paint-bucket" class="me-2" />
|
||||||
|
<span class="font-semibold">Tambah</span> Alat Kesehatan
|
||||||
|
</div>
|
||||||
|
<AppMaterialEntryForm v-model="data" :errors="errors" />
|
||||||
|
<div class="my-2 flex justify-end py-2">
|
||||||
|
<Action @click="onClick" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<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'
|
||||||
|
|
||||||
|
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: 'BMHP',
|
||||||
|
icon: 'i-lucide-paint-bucket',
|
||||||
|
addNav: {
|
||||||
|
label: 'Tambah',
|
||||||
|
onClick: () => navigateTo('/tools-equipment-src/material/add'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getMaterialList()
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('rec_id', recId)
|
||||||
|
provide('rec_action', recAction)
|
||||||
|
provide('rec_item', recItem)
|
||||||
|
provide('table_data_loader', isLoading)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rounded-md border p-4">
|
||||||
|
<Header :prep="{ ...headerPrep }" :ref-search-nav="refSearchNav" />
|
||||||
|
<div class="rounded-md border p-4">
|
||||||
|
<AppMaterialList v-if="!isLoading.dataListLoading" :data="data" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -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: 'Tambah User',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
||||||
|
|
||||||
|
const { checkRole, hasCreateAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
if (!hasAccess) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 403,
|
||||||
|
statusMessage: 'Access denied',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canCreate = hasCreateAccess(roleAccess)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canCreate">
|
||||||
|
<FlowDeviceEntry />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<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 User',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
||||||
|
|
||||||
|
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)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<FlowDeviceList />
|
||||||
|
</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/base/error/error.vue'
|
||||||
|
import { PAGE_PERMISSIONS } from '~/lib/page-permission'
|
||||||
|
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['rbac'],
|
||||||
|
roles: ['doctor', 'nurse', 'admisi', 'pharmacy', 'billing', 'management'],
|
||||||
|
title: 'Tambah User',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
||||||
|
|
||||||
|
const { checkRole, hasCreateAccess } = useRBAC()
|
||||||
|
|
||||||
|
// Check if user has access to this page
|
||||||
|
const hasAccess = checkRole(roleAccess)
|
||||||
|
if (!hasAccess) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 403,
|
||||||
|
statusMessage: 'Access denied',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define permission-based computed properties
|
||||||
|
const canCreate = hasCreateAccess(roleAccess)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="canCreate">
|
||||||
|
<FlowMaterialEntry />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<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 User',
|
||||||
|
contentFrame: 'cf-full-width',
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: () => route.meta.title as string,
|
||||||
|
})
|
||||||
|
|
||||||
|
const roleAccess: PagePermission = PAGE_PERMISSIONS['/doctor']
|
||||||
|
|
||||||
|
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)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="canRead">
|
||||||
|
<FlowMaterialList />
|
||||||
|
</div>
|
||||||
|
<Error v-else :status-code="403" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
+1
-1
@@ -67,6 +67,6 @@
|
|||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
"vue-sonner": "^1.3.0",
|
"vue-sonner": "^1.3.0",
|
||||||
"vue-tsc": "^2.1.10",
|
"vue-tsc": "^2.1.10",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.25.76"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+1
-1
@@ -161,7 +161,7 @@ devDependencies:
|
|||||||
specifier: ^2.1.10
|
specifier: ^2.1.10
|
||||||
version: 2.2.12([email protected])
|
version: 2.2.12([email protected])
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.24.2
|
specifier: ^3.25.76
|
||||||
version: 3.25.76
|
version: 3.25.76
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|||||||
Reference in New Issue
Block a user