Merge branch 'dev' into fe-prescription-56

This commit is contained in:
Andrian Roshandy
2025-10-13 06:29:03 +07:00
138 changed files with 4025 additions and 792 deletions
+1 -1
View File
@@ -174,7 +174,7 @@ body {
}
body, table, label {
@apply md:!text-xs xl:!text-sm 2xl:!text-sm;
@apply md:!text-xs 2xl:!text-sm;
}
/* Container */
+56 -11
View File
@@ -5,6 +5,10 @@ import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Types
import type { InfraFormData } from '~/schemas/infra.schema'
@@ -16,6 +20,7 @@ import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
parents: any[]
values: any
isLoading?: boolean
isReadonly?: boolean
@@ -34,27 +39,28 @@ const { defineField, errors, meta } = useForm({
initialValues: {
code: '',
name: '',
infraGroup_code: 'counter',
infraGroup_code: infraGroupCodesKeys.bed,
parent_id: null,
} as Partial<InfraFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [infraGroup_code, infraGroupAttrs] = defineField('infraGroup_code')
const [infraGroup_code] = defineField('infraGroup_code')
const [parent_id, parentIdAttrs] = defineField('parent_id')
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
if (props.values.parent_id !== undefined)
parent_id.value = props.values.parent_id ? Number(props.values.parent_id) : null
}
const resetForm = () => {
code.value = ''
name.value = ''
infraGroup_code.value = 'counter'
infraGroup_code.value = infraGroupCodesKeys.bed
parent_id.value = null
}
@@ -62,8 +68,8 @@ function onSubmitForm() {
const formData: InfraFormData = {
code: code.value || '',
name: name.value || '',
infraGroup_code: infraGroup_code.value || 'counter',
parent_id: parent_id.value || null,
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.bed,
parent_id: parent_id.value ? Number(parent_id.value) : null,
}
emit('submit', formData, resetForm)
}
@@ -74,23 +80,62 @@ function onCancelForm() {
</script>
<template>
<form id="form-counter" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-floor"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Kamar</Label>
<Field :errMessage="errors.parent_id">
<Combobox
id="parent"
v-model="parent_id"
v-bind="parentIdAttrs"
:items="parents"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Kamar"
search-placeholder="Cari Kamar"
empty-message="Item tidak ditemukan"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+2 -5
View File
@@ -12,9 +12,9 @@ type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [{ width: 100 }, {}, {}, { width: 50 }]
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Counter Induk' }, { label: '' }]]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Kamar' }, { label: '' }]]
export const keys = ['code', 'name', 'parent', 'action']
@@ -36,9 +36,6 @@ export const funcComponent: RecStrFuncComponent = {
idx,
rec: rec as object,
component: action,
props: {
size: 'sm',
},
}
return res
},
+8 -3
View File
@@ -1,6 +1,11 @@
<script setup lang="ts">
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Components
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Configs
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
interface Props {
@@ -21,7 +26,7 @@ function handlePageChange(page: number) {
<template>
<div class="space-y-4">
<PubBaseDataTable
<PubMyUiDataTable
:rows="data"
:cols="cols"
:header="header"
@@ -33,4 +38,4 @@ function handlePageChange(page: number) {
/>
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
</template>
</template>
+56 -11
View File
@@ -5,6 +5,10 @@ import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Types
import type { InfraFormData } from '~/schemas/infra.schema'
@@ -16,6 +20,7 @@ import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
parents: any[]
values: any
isLoading?: boolean
isReadonly?: boolean
@@ -34,27 +39,28 @@ const { defineField, errors, meta } = useForm({
initialValues: {
code: '',
name: '',
infraGroup_code: 'counter',
infraGroup_code: infraGroupCodesKeys.chamber,
parent_id: null,
} as Partial<InfraFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [infraGroup_code, infraGroupAttrs] = defineField('infraGroup_code')
const [infraGroup_code] = defineField('infraGroup_code')
const [parent_id, parentIdAttrs] = defineField('parent_id')
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
if (props.values.parent_id !== undefined)
parent_id.value = props.values.parent_id ? Number(props.values.parent_id) : null
}
const resetForm = () => {
code.value = ''
name.value = ''
infraGroup_code.value = 'counter'
infraGroup_code.value = infraGroupCodesKeys.chamber
parent_id.value = null
}
@@ -62,8 +68,8 @@ function onSubmitForm() {
const formData: InfraFormData = {
code: code.value || '',
name: name.value || '',
infraGroup_code: infraGroup_code.value || 'counter',
parent_id: parent_id.value || null,
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.chamber,
parent_id: parent_id.value ? Number(parent_id.value) : null,
}
emit('submit', formData, resetForm)
}
@@ -74,23 +80,62 @@ function onCancelForm() {
</script>
<template>
<form id="form-counter" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-floor"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Lantai</Label>
<Field :errMessage="errors.parent_id">
<Combobox
id="parent"
v-model="parent_id"
v-bind="parentIdAttrs"
:items="parents"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Lantai"
search-placeholder="Cari Lantai"
empty-message="Item tidak ditemukan"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+2 -5
View File
@@ -12,9 +12,9 @@ type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [{ width: 100 }, {}, {}, { width: 50 }]
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Counter Induk' }, { label: '' }]]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Lantai' }, { label: '' }]]
export const keys = ['code', 'name', 'parent', 'action']
@@ -36,9 +36,6 @@ export const funcComponent: RecStrFuncComponent = {
idx,
rec: rec as object,
component: action,
props: {
size: 'sm',
},
}
return res
},
+7 -2
View File
@@ -1,6 +1,11 @@
<script setup lang="ts">
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Components
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Configs
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
interface Props {
@@ -21,7 +26,7 @@ function handlePageChange(page: number) {
<template>
<div class="space-y-4">
<PubBaseDataTable
<PubMyUiDataTable
:rows="data"
:cols="cols"
:header="header"
+3 -3
View File
@@ -37,7 +37,7 @@ const { defineField, errors, meta } = useForm({
initialValues: {
code: '',
name: '',
infraGroup_code: infraGroupCodesKeys.building,
infraGroup_code: infraGroupCodesKeys.counter,
parent_id: null,
} as Partial<InfraFormData>,
})
@@ -57,7 +57,7 @@ if (props.values) {
const resetForm = () => {
code.value = ''
name.value = ''
infraGroup_code.value = infraGroupCodesKeys.building
infraGroup_code.value = infraGroupCodesKeys.counter
parent_id.value = null
}
@@ -65,7 +65,7 @@ function onSubmitForm() {
const formData: InfraFormData = {
code: code.value || '',
name: name.value || '',
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.building,
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.counter,
parent_id: parent_id.value || null,
}
emit('submit', formData, resetForm)
@@ -0,0 +1,207 @@
<script setup lang="ts">
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Types
import type { DivisionPositionFormData } from '~/schemas/division-position.schema'
// Helpers
import type z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { genBase } from '~/models/_base'
import { genDivisionPosition } from '~/models/division-position'
interface Props {
schema: z.ZodSchema<any>
divisions: any[]
employees: any[]
values: any
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const isLoading = props.isLoading !== undefined ? props.isLoading : false
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
const emit = defineEmits<{
submit: [values: DivisionPositionFormData, resetForm: () => void]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: genDivisionPosition() as Partial<DivisionPositionFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [division, divisionAttrs] = defineField('division_id')
const [employee, employeeAttrs] = defineField('employee_id')
const [headStatus, headStatusAttrs] = defineField('headStatus')
// RadioGroup uses string values; expose a string computed that maps to the boolean field
const headStatusStr = computed<string>({
get() {
if (headStatus.value === true) return 'true'
if (headStatus.value === false) return 'false'
return ''
},
set(v: string) {
if (v === 'true') headStatus.value = true
else if (v === 'false') headStatus.value = false
else headStatus.value = undefined
},
})
// Fill fields from props.values if provided
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.division_id !== undefined)
division.value = props.values.division_id ? Number(props.values.division_id) : null
if (props.values.employee_id !== undefined)
employee.value = props.values.employee_id ? Number(props.values.employee_id) : null
if (props.values.headStatus !== undefined) headStatus.value = !!props.values.headStatus
}
const resetForm = () => {
code.value = ''
name.value = ''
division.value = null
employee.value = null
headStatus.value = false
}
// Form submission handler
function onSubmitForm() {
const formData: DivisionPositionFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
division_id: division.value || null,
employee_id: employee.value || null,
headStatus: headStatus.value !== undefined ? headStatus.value : undefined,
}
emit('submit', formData, resetForm)
}
// Form cancel handler
function onCancelForm() {
emit('cancel', resetForm)
}
</script>
<template>
<form
id="form-division-position"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Posisi Divisi</Label>
<Field :errMessage="errors.division_id">
<Combobox
id="division"
v-model="division"
v-bind="divisionAttrs"
:items="divisions"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Divisi"
search-placeholder="Cari Divisi"
empty-message="Item tidak ditemukan"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Karyawan</Label>
<Field :errMessage="errors.employee_id">
<Combobox
id="employee"
v-model="employee"
v-bind="employeeAttrs"
:items="employees"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Karyawan"
search-placeholder="Cari Karyawan"
empty-message="Item tidak ditemukan"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Status Kepala</Label>
<Field :errMessage="errors.headStatus">
<RadioGroup
v-model="headStatusStr"
v-bind="headStatusAttrs"
class="flex gap-4"
>
<div class="flex items-center space-x-2">
<RadioGroupItem
id="head-yes"
value="true"
/>
<Label for="head-yes">Ya</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroupItem
id="head-no"
value="false"
/>
<Label for="head-no">Tidak</Label>
</div>
</RadioGroup>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
class="w-[120px]"
:disabled="isLoading || !meta.valid"
@click="onSubmitForm"
>
Simpan
</Button>
</div>
</form>
</template>
@@ -0,0 +1,64 @@
import type {
Col,
KeyLabel,
RecComponent,
RecStrFuncComponent,
RecStrFuncUnknown,
Th,
} from '~/components/pub/my-ui/data/types'
import { defineAsyncComponent } from 'vue'
type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-ud.vue'))
export const cols: Col[] = [{}, {}, {}, {}, {}, { width: 50 }]
export const header: Th[][] = [
[
{ label: 'Kode' },
{ label: 'Nama' },
{ label: 'Divisi Induk' },
{ label: 'Karyawan' },
{ label: 'Status Kepala' },
{ label: '' },
],
]
export const keys = ['code', 'name', 'division', 'employee', 'head', 'action']
export const delKeyNames: KeyLabel[] = [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
]
export const funcParsed: RecStrFuncUnknown = {
division: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.division?.name || '-'
},
employee: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.employee?.name || '-'
},
head: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.headStatus ? 'Ya' : 'Tidak'
},
}
export const funcComponent: RecStrFuncComponent = {
action(rec, idx) {
const res: RecComponent = {
idx,
rec: rec as object,
component: action,
props: {
size: 'sm',
},
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {}
@@ -0,0 +1,41 @@
<script setup lang="ts">
// Components
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Configs
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
interface Props {
data: any[]
paginationMeta: PaginationMeta
}
defineProps<Props>()
const emit = defineEmits<{
pageChange: [page: number]
}>()
function handlePageChange(page: number) {
emit('pageChange', page)
}
</script>
<template>
<div class="space-y-4">
<PubMyUiDataTable
:rows="data"
:cols="cols"
:header="header"
:keys="keys"
:func-parsed="funcParsed"
:func-html="funcHtml"
:func-component="funcComponent"
:skeleton-size="paginationMeta?.pageSize"
/>
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
</template>
+34 -13
View File
@@ -7,12 +7,13 @@ import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import TreeSelect from '~/components/pub/my-ui/select-tree/tree-select.vue'
// Types
import type { DivisionFormData } from '~/schemas/division.schema.ts'
import type { DivisionFormData } from '~/schemas/division.schema'
// Helpers
import type z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { genBase } from '~/models/_base'
interface Props {
schema: z.ZodSchema<any>
@@ -36,37 +37,33 @@ const { defineField, errors, meta } = useForm({
code: '',
name: '',
parent_id: null,
division_id: null,
} as Partial<DivisionFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [parent, parentAttrs] = defineField('parent_id')
const [division] = defineField('division_id')
// Fill fields from props.values if provided
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.parent_id !== undefined) parent.value = String(props.values.parent_id)
if (props.values.division_id !== undefined) division.value = String(props.values.division_id)
}
const resetForm = () => {
code.value = ''
name.value = ''
parent.value = null
division.value = null
}
// Form submission handler
function onSubmitForm() {
const formData: DivisionFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
parent_id: parent.value || null,
division_id: division.value || null,
}
emit('submit', formData, resetForm)
}
@@ -78,25 +75,42 @@ function onCancelForm() {
</script>
<template>
<form id="form-division" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-division"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Divisi Induk</Label>
<Field :errMessage="errors.division">
<Field :errMessage="errors.parent_id">
<TreeSelect
id="division"
id="parent"
v-model="parent"
v-bind="parentAttrs"
:data="divisions"
@@ -110,7 +124,14 @@ function onCancelForm() {
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+2 -2
View File
@@ -5,9 +5,9 @@ import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
import { Form } from '~/components/pub/ui/form'
import DatepickerSingle from '~/components/pub/my-ui/form/datepicker-single.vue'
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
import { educationCodes, genderCodes, occupationCodes, religionCodes, relationshipCodes } from '~/lib/constants'
import { mapToComboboxOptList } from '~/lib/utils'
+1 -1
View File
@@ -7,7 +7,7 @@ defineProps<{
</script>
<template>
<div class="w-full rounded-md border bg-white p-4 shadow-sm">
<div class="w-full rounded-md border bg-white dark:bg-neutral-950 p-4 shadow-sm">
<!-- Data Pasien -->
<h2 class="mb-2 md:text-base 2xl:text-lg font-semibold">{{ 'data.patient.person.name' }} - {{ 'data.patient.number' }}</h2>
+39 -7
View File
@@ -6,12 +6,13 @@ import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
// Types
import type { MaterialFormData } from '~/schemas/material.schema.ts'
import type { MaterialFormData } from '~/schemas/material.schema'
// Helpers
import type z from 'zod'
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
import { genBase } from '~/models/_base'
interface Props {
schema: z.ZodSchema<any>
@@ -61,6 +62,7 @@ const resetForm = () => {
function onSubmitForm() {
const formData: MaterialFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
uom_code: uom.value || '',
@@ -75,18 +77,35 @@ function onCancelForm() {
</script>
<template>
<form id="form-equipment" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-equipment"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
@@ -106,12 +125,25 @@ function onCancelForm() {
<Cell>
<Label height="compact">Stok</Label>
<Field :errMessage="errors.stock">
<Input id="stock" v-model="stock" type="number" v-bind="stockAttrs" :disabled="isLoading || isReadonly" />
<Input
id="stock"
v-model="stock"
type="number"
v-bind="stockAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+34 -9
View File
@@ -5,10 +5,10 @@ import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Constants
import { infraGroupCodesKeys } from "~/lib/constants"
import { infraGroupCodesKeys } from '~/lib/constants'
// Types
import type { InfraFormData } from '~/schemas/infra.schema'
@@ -53,7 +53,8 @@ if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.parent_id !== undefined) parent_id.value = String(props.values.parent_id)
if (props.values.parent_id !== undefined)
parent_id.value = props.values.parent_id ? Number(props.values.parent_id) : null
}
const resetForm = () => {
@@ -79,18 +80,35 @@ function onCancelForm() {
</script>
<template>
<form id="form-floor" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-floor"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
@@ -101,7 +119,7 @@ function onCancelForm() {
v-model="parent_id"
v-bind="parentIdAttrs"
:items="parents"
:disabled="isLoading || isReadonly"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Gedung"
search-placeholder="Cari Gedung"
empty-message="Item tidak ditemukan"
@@ -110,7 +128,14 @@ function onCancelForm() {
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+31 -7
View File
@@ -4,10 +4,10 @@ import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Types
import type { InstallationFormData } from '~/schemas/installation.schema.ts'
import type { InstallationFormData } from '~/schemas/installation.schema'
// Helpers
import type z from 'zod'
@@ -73,18 +73,35 @@ function onCancelForm() {
</script>
<template>
<form id="form-unit" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-unit"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
@@ -104,7 +121,14 @@ function onCancelForm() {
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
@@ -1,4 +1,3 @@
<script setup lang="ts">
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
@@ -8,7 +7,7 @@ import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
// Types
import type { BaseFormData } from '~/schemas/base.schema.ts'
import type { BaseFormData } from '~/schemas/base.schema'
// Helpers
import type z from 'zod'
@@ -65,23 +64,47 @@ function onCancelForm() {
</script>
<template>
<form id="form-medicine-group" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-medicine-group"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
@@ -1,4 +1,3 @@
<script setup lang="ts">
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
@@ -8,7 +7,7 @@ import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
// Types
import type { BaseFormData } from '~/schemas/base.schema.ts'
import type { BaseFormData } from '~/schemas/base.schema'
// Helpers
import type z from 'zod'
@@ -65,23 +64,47 @@ function onCancelForm() {
</script>
<template>
<form id="form-medicine-method" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-medicine-method"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FormErrors } from '~/types/error'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
+1 -1
View File
@@ -3,7 +3,7 @@ import type { FormErrors } from '~/types/error'
import { Form } from '~/components/pub/ui/form'
import { toTypedSchema } from '@vee-validate/zod'
import Block from '~/components/pub/my-ui/form/block.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
@@ -51,7 +51,7 @@ defineProps<Props>()
<DE.Cell>
<DE.Label>DPJP</DE.Label>
<DE.Field>
{{ item.doctor.employee.person.name }}
{{ item.doctor?.employee?.person.name }}
</DE.Field>
</DE.Cell>
<DE.Cell>
@@ -0,0 +1,108 @@
<script setup lang="ts">
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
// Constants
import { infraGroupCodesKeys } from "~/lib/constants"
// Types
import type { InfraFormData } from '~/schemas/infra.schema'
// Helpers
import type z from 'zod'
import { useForm } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
values: any
isLoading?: boolean
isReadonly?: boolean
}
const props = defineProps<Props>()
const isLoading = props.isLoading !== undefined ? props.isLoading : false
const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
const emit = defineEmits<{
submit: [values: InfraFormData, resetForm: () => void]
cancel: [resetForm: () => void]
}>()
const { defineField, errors, meta } = useForm({
validationSchema: toTypedSchema(props.schema),
initialValues: {
code: '',
name: '',
infraGroup_code: infraGroupCodesKeys['public-screen'],
parent_id: null,
} as Partial<InfraFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [infraGroup_code] = defineField('infraGroup_code')
const [parent_id] = defineField('parent_id')
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
}
const resetForm = () => {
code.value = ''
name.value = ''
infraGroup_code.value = infraGroupCodesKeys['public-screen']
parent_id.value = null
}
function onSubmitForm() {
const formData: InfraFormData = {
code: code.value || '',
name: name.value || '',
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys['public-screen'],
parent_id: parent_id.value || null,
}
emit('submit', formData, resetForm)
}
function onCancelForm() {
emit('cancel', resetForm)
}
</script>
<template>
<form id="form-building" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
v-if="!isReadonly"
type="button"
class="w-[120px]"
:disabled="isLoading || !meta.valid"
@click="onSubmitForm"
>
Simpan
</Button>
</div>
</form>
</template>
@@ -0,0 +1,37 @@
import type {
Col,
KeyLabel,
RecComponent,
RecStrFuncComponent,
RecStrFuncUnknown,
Th,
} from '~/components/pub/my-ui/data/types'
import { defineAsyncComponent } from 'vue'
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [{}, {}, { width: 50 }]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: '' }]]
export const keys = ['code', 'name', 'action']
export const delKeyNames: KeyLabel[] = [
{ key: 'code', label: 'Kode' },
{ key: 'name', label: 'Nama' },
]
export const funcParsed: RecStrFuncUnknown = {}
export const funcComponent: RecStrFuncComponent = {
action(rec, idx) {
const res: RecComponent = {
idx,
rec: rec as object,
component: action,
}
return res
},
}
export const funcHtml: RecStrFuncUnknown = {}
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
// Components
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Configs
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
interface Props {
data: any[]
paginationMeta: PaginationMeta
}
defineProps<Props>()
const emit = defineEmits<{
pageChange: [page: number]
}>()
function handlePageChange(page: number) {
emit('pageChange', page)
}
</script>
<template>
<div class="space-y-4">
<PubMyUiDataTable
:rows="data"
:cols="cols"
:header="header"
:keys="keys"
:func-parsed="funcParsed"
:func-html="funcHtml"
:func-component="funcComponent"
:skeleton-size="paginationMeta?.pageSize"
/>
<PaginationView :pagination-meta="paginationMeta" @page-change="handlePageChange" />
</div>
</template>
+52 -28
View File
@@ -5,7 +5,7 @@ import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
@@ -65,12 +65,12 @@ if (props.values) {
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.specialist_id !== undefined)
specialist_id.value = props.values.specialist_id ? String(props.values.specialist_id) : null
specialist_id.value = props.values.specialist_id ? Number(props.values.specialist_id) : null
if (props.values.subspecialist_id !== undefined)
subspecialist_id.value = props.values.subspecialist_id ? String(props.values.subspecialist_id) : null
if (props.values.unit_id !== undefined) unit_id.value = props.values.unit_id ? String(props.values.unit_id) : null
subspecialist_id.value = props.values.subspecialist_id ? Number(props.values.subspecialist_id) : null
if (props.values.unit_id !== undefined) unit_id.value = props.values.unit_id ? Number(props.values.unit_id) : null
if (props.values.parent_id !== undefined)
parent_id.value = props.values.parent_id ? String(props.values.parent_id) : null
parent_id.value = props.values.parent_id ? Number(props.values.parent_id) : null
}
const resetForm = () => {
@@ -102,18 +102,50 @@ function onCancelForm() {
</script>
<template>
<form id="form-building" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-building"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Lantai</Label>
<Field :errMessage="errors.parent_id">
<Combobox
id="parent"
v-model="parent_id"
v-bind="parentAttrs"
:items="parents"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Lantai"
search-placeholder="Cari Lantai"
empty-message="Lantai tidak ditemukan"
/>
</Field>
</Cell>
<Cell>
@@ -124,7 +156,7 @@ function onCancelForm() {
v-model="unit_id"
v-bind="unitAttrs"
:items="units"
:disabled="isLoading || isReadonly"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Unit"
search-placeholder="Cari Unit"
empty-message="Unit tidak ditemukan"
@@ -140,7 +172,7 @@ function onCancelForm() {
v-model="specialist_id"
v-bind="specialistAttrs"
:items="specialists"
:disabled="isLoading || isReadonly"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Spesialis"
search-placeholder="Cari spesialis"
empty-message="Spesialis tidak ditemukan"
@@ -156,31 +188,23 @@ function onCancelForm() {
v-model="subspecialist_id"
v-bind="subspecialistAttrs"
:items="subspecialists"
:disabled="isLoading || isReadonly"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Sub Spesialis"
search-placeholder="Cari sub spesialis"
empty-message="Sub Spesialis tidak ditemukan"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Lantai</Label>
<Field :errMessage="errors.parent_id">
<Combobox
id="parent"
v-model="parent_id"
v-bind="parentAttrs"
:items="parents"
:disabled="isLoading || isReadonly"
placeholder="Pilih Lantai"
search-placeholder="Cari Lantai"
empty-message="Lantai tidak ditemukan"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+6 -11
View File
@@ -14,18 +14,9 @@ const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dr
export const cols: Col[] = [{}, {}, {}, {}, {}, { width: 50 }]
export const header: Th[][] = [
[
{ label: 'Kode' },
{ label: 'Nama' },
{ label: 'Spesialis' },
{ label: 'Sub Spesialis' },
{ label: 'Unit' },
{ label: '' },
],
]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Lantai' }, { label: '' }]]
export const keys = ['code', 'name', 'specialist', 'subspecialist', 'unit', 'action']
export const keys = ['code', 'name', 'parent', 'action']
export const delKeyNames: KeyLabel[] = [
{ key: 'code', label: 'Kode' },
@@ -45,6 +36,10 @@ export const funcParsed: RecStrFuncUnknown = {
const recX = rec as SmallDetailDto
return recX.unit?.name || '-'
},
parent: (rec: unknown): unknown => {
const recX = rec as SmallDetailDto
return recX.parent?.name || '-'
},
}
export const funcComponent: RecStrFuncComponent = {
+1 -1
View File
@@ -10,7 +10,7 @@ import { Label } from '~/components/pub/ui/label'
import Select from '~/components/pub/ui/select/Select.vue'
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
import { Textarea } from '~/components/pub/ui/textarea'
import DatepickerSingle from '~/components/pub/my-ui/form/datepicker-single.vue'
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
const emit = defineEmits<{
(e: 'event', value: any): void
+2 -2
View File
@@ -2,12 +2,12 @@
import type { FormErrors } from '~/types/error'
import { toTypedSchema } from '@vee-validate/zod'
import Block from '~/components/pub/my-ui/form/block.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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 { Form } from '~/components/pub/ui/form'
import DatepickerSingle from '~/components/pub/my-ui/form/datepicker-single.vue'
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
import { educationCodes, genderCodes, occupationCodes, religionCodes, relationshipCodes } from '~/lib/constants'
import { mapToComboboxOptList } from '~/lib/utils'
+319 -66
View File
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { ref } from 'vue'
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
@@ -9,6 +8,8 @@ const props = defineProps<{
excludeFields?: string[]
}>()
const emits = defineEmits(['click'])
const subject = ref({
'prim-compl': '',
'sec-compl': '',
@@ -25,77 +26,329 @@ const isExcluded = (key: string) => props.excludeFields?.includes(key)
</script>
<template>
<div class="space-y-4">
<!-- Keluhan Utama -->
<Cell v-if="!isExcluded('prim-compl')">
<Label dynamic>Keluhan Utama</Label>
<Field>
<Textarea placeholder="Masukkan anamnesa pasien" v-model="subject['prim-compl']" />
</Field>
</Cell>
<form id="entry-form">
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<div>
<h1 class="font-semibold">Pemeriksaan Fisiatrik</h1>
<span class="text-sm font-semibold">Anamnesa</span>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block>
<Cell>
<Label dynamic>Keluhan Utama</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<!-- Keluhan Tambahan -->
<Cell v-if="!isExcluded('sec-compl')">
<Label dynamic>Keluhan Tambahan</Label>
<Field>
<Textarea placeholder="Masukkan keluhan tambahan" v-model="subject['sec-compl']" />
</Field>
</Cell>
<Block :colCount="3">
<Cell>
<Label dynamic>Diagnosa Medis</Label>
<Field>
<Textarea />
</Field>
</Cell>
<!-- Riwayat Penyakit Sekarang -->
<Cell v-if="!isExcluded('cur-disea-hist')">
<Label dynamic>Riwayat Penyakit Sekarang</Label>
<Field>
<Textarea placeholder="Masukkan riwayat penyakit sekarang" v-model="subject['cur-disea-hist']" />
</Field>
</Cell>
<Cell>
<Label dynamic>Rencana Awal Medis</Label>
<Field>
<Textarea />
</Field>
</Cell>
<!-- Riwayat Penyakit Dahulu -->
<Cell v-if="!isExcluded('pas-disea-hist')">
<Label dynamic>Riwayat Penyakit Dahulu</Label>
<Field>
<Textarea placeholder="Masukkan riwayat penyakit dahulu" v-model="subject['pas-disea-hist']" />
</Field>
</Cell>
<Cell>
<Label dynamic>Terapi</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
</div>
<Separator class="mt-8" />
<!-- Riwayat Penyakit Keluarga -->
<Cell v-if="!isExcluded('fam-disea-hist')">
<Label dynamic>Riwayat Penyakit Keluarga</Label>
<Field>
<Textarea placeholder="Masukkan riwayat penyakit keluarga" v-model="subject['fam-disea-hist']" />
</Field>
</Cell>
<div class="my-2">
<h1 class="font-semibold">Pemeriksaan Fisik</h1>
<span class="text-sm font-semibold">Status Generalis</span>
</div>
<!-- Riwayat Alergi -->
<Cell v-if="!isExcluded('alg-hist')">
<Label dynamic>Riwayat Alergi</Label>
<Field>
<Textarea placeholder="Masukkan riwayat alergi" v-model="subject['alg-hist']" />
</Field>
</Cell>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Tekanan Darah</Label>
<Field>
<Input placeholder="Sistolik" />
<Input placeholder="Diastolik" />
</Field>
</Cell>
<!-- Reaksi Alergi -->
<Cell v-if="!isExcluded('alg-react')">
<Label dynamic>Reaksi Alergi</Label>
<Field>
<Textarea placeholder="Masukkan reaksi alergi" v-model="subject['alg-react']" />
</Field>
</Cell>
<Cell>
<Label dynamic>Nadi</Label>
<Field>
<Input />
</Field>
</Cell>
<!-- Pengobatan yang Sedang Dijalani -->
<Cell v-if="!isExcluded('med-hist')">
<Label dynamic>Riwayat Pengobatan</Label>
<Field>
<Textarea placeholder="Masukkan pengobatan yang sedang dijalani" v-model="subject['med-hist']" />
</Field>
</Cell>
<Cell>
<Label dynamic>GCS</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="3">
<Cell>
<Label dynamic>RR</Label>
<Field>
<Input />
</Field>
</Cell>
<!-- Golongan Darah -->
<Cell v-if="!isExcluded('blood-type')">
<Label dynamic>Golongan Darah</Label>
<Field>
<Input type="text" placeholder="Masukkan golongan darah" v-model="subject['blood-type']" />
</Field>
</Cell>
</div>
<Cell>
<Label dynamic>Ambulasi</Label>
<Field>
<RadioGroup class="flex gap-4">
<div class="flex items-center space-x-2">
<RadioGroupItem
id="vaksin-yes"
value="yes"
/>
<Label for="vaksin-yes">Dependent</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroupItem
id="vaksin-no"
value="no"
/>
<Label for="vaksin-no">Independen</Label>
</div>
</RadioGroup>
</Field>
</Cell>
<Cell>
<Label dynamic>Gait</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
</div>
<div class="my-2">
<span class="text-sm font-semibold">Status Neurologis</span>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>N. Cranialis</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Sensoris</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Reflek Fisilogis</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Reflek Patologis</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Saraf Otonom</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2">
<span class="text-sm font-semibold">Status Muskuloskeletal - ROM</span>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="2">
<Cell>
<Label dynamic>Leher</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Batang Tubuh</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>AGA</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>AGB</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2">
<span class="text-sm font-semibold">Status Muskuloskeletal - MMT</span>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="2">
<Cell>
<Label dynamic>Leher</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Batang Tubuh</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>AGA</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>AGB</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2">
<span class="text-sm font-semibold">Statu Lokalis</span>
</div>
<Block>
<Cell>
<Label dynamic>Deskripsi Temuan Fisik</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Separator class="mt-8" />
<div class="my-2">
<h1 class="font-semibold">Pemeriksaan Penunjang</h1>
</div>
<Block>
<Cell>
<Label dynamic>Catatan Pemeriksaan Penunjang</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Separator class="mt-8" />
<div class="my-2">
<h1 class="font-semibold">Daftar Masalah</h1>
</div>
<Block :colCount="2">
<Cell>
<Label dynamic>Masalah Medik</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Masalah Rehabilitasi Medik</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Separator class="mt-8" />
<div class="my-2">
<h1 class="font-semibold">Diagnosa Fungsional (ICD-X)</h1>
</div>
<div class="mb-8 grid grid-cols-2 gap-4">
<AppIcdPreview />
</div>
<div class="my-2">
<h1 class="font-semibold">Program Rehabilitasi</h1>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Modalitas Fisik</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Latihan</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Ortesa Protesa</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Edukasi</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Lain-Lain</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
</div>
</div>
</form>
</template>
+471 -157
View File
@@ -1,172 +1,486 @@
<script setup lang="ts">
import Block from '~/components/pub/my-ui/form/block.vue'
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 Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
const props = defineProps<{
excludeFields?: string[]
}>()
const emits = defineEmits(['click'])
const subject = ref({
'prim-compl': '',
'sec-compl': '',
'cur-disea-hist': '',
'pas-disea-hist': '',
'fam-disea-hist': '',
'alg-hist': '',
'alg-react': '',
'med-hist': '',
'blood-type': '',
})
const isExcluded = (key: string) => props.excludeFields?.includes(key)
const disorders = ref<string[]>([])
const therapies = ref<string[]>([])
const summary = ref('')
const disorderOptions = [
'Fungsi Otot',
'Fungsi Sendi',
'Fungsi Jalan',
'Fungsi Syaraf',
'Fungsi Koordinasi',
'Jantung',
'Fungsi Respirasi',
'Fungsi Menelan',
'Fungsi Bladder',
'Fungsi Bowel',
'Fungsi Luhur',
'Fungsi Kontrol Postur',
'Fungsi Eksekusi',
'Fungsi Ortosa/Protesa',
'Gangguan Aktivitas Sehari-hari',
'Lainnya',
]
const therapyOptions = ['Terapi Latihan', 'Modalitas Fisik', 'Protesa/Ortosa', 'Medikamentosa', 'Lain-lain: Konsultasi']
</script>
<template>
<form id="entry-form">
<div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
<Block>
<FieldGroup>
<Label dynamic>Sudah Vaksin</Label>
<Field>
<Select
:options="[
{ label: 'Sudah', value: 'yes' },
{ label: 'Belum', value: 'no' },
]"
/>
</Field>
</FieldGroup>
<FieldGroup>
<Label dynamic>Kasus</Label>
<Field>
<Select
:options="[
{ label: 'Baru', value: 'baru' },
{ label: 'Lama', value: 'lama' },
]"
/>
</Field>
</FieldGroup>
<FieldGroup>
<Label dynamic>Kunjungan</Label>
<Field>
<Select
:options="[
{ label: 'Baru', value: 'baru' },
{ label: 'Lama', value: 'lama' },
]"
/>
</Field>
</FieldGroup>
<!-- Riwayat Penyakit -->
<FieldGroup :column="2">
<Cell>
<Label dynamic>Keluhan Utama</Label>
<Field>
<Textarea placeholder="Masukkan anamnesa pasien" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Riwayat Penyakit</Label>
<Field>
<Textarea
placeholder="Masukkan anamnesa pasien (Riwayat Penyakit Sekarang, Dahulu, Pengobatan, Keluarga, dll)"
/>
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>SpO₂</Label>
<Field>
<Input type="number" placeholder="%" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Tekanan Darah Sistol</Label>
<Field>
<Input type="number" placeholder="mmHg" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Tekanan Darah Diastol</Label>
<Field>
<Input type="number" placeholder="mmHg" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Respirasi</Label>
<Field>
<Input type="number" placeholder="kali/menit" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Nadi</Label>
<Field>
<Input type="number" placeholder="kali/menit" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Temperatur</Label>
<Field>
<Input type="number" placeholder="℃" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Berat Badan</Label>
<Field>
<Input type="number" placeholder="Kg" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Tinggi Badan</Label>
<Field>
<Input type="number" placeholder="Cm" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Golongan Darah</Label>
<Field>
<Select :options="bloodGroups" />
</Field>
</FieldGroup>
<FieldGroup :column="2">
<Label dynamic>Pemeriksaan Fisik (Yang Mendukung)</Label>
<Field>
<Textarea placeholder="Masukkan pemeriksaan fisik" />
</Field>
</FieldGroup>
<!-- Prosedur -->
<FieldGroup :column="2">
<Label dynamic>Diagnosa (ICD-X)</Label>
<Field>
<button class="rounded bg-orange-100 px-3 py-1 text-orange-600">+ Pilih Diagnosa</button>
</Field>
</FieldGroup>
<!-- Diagnosa -->
<FieldGroup :column="2">
<Label dynamic>Diagnosa (ICD-X)</Label>
<Field>
<button class="rounded bg-orange-100 px-3 py-1 text-orange-600">+ Pilih Diagnosa</button>
</Field>
</FieldGroup>
<FieldGroup :column="3">
<Label dynamic>Diagnosa Medis</Label>
<Field>
<Textarea />
</Field>
</FieldGroup>
<FieldGroup :column="3">
<Label dynamic>Rencana Awal Medis</Label>
<Field>
<Textarea />
</Field>
</FieldGroup>
<FieldGroup :column="3">
<Label dynamic>Terapi</Label>
<Field>
<Textarea />
</Field>
</FieldGroup>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Riwayat Penyakit Dahulu</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Riwayat Penyakit Sekarang</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Separator class="mt-8" />
<div class="my-2">
<h1 class="font-semibold">Pemeriksaan Fisik</h1>
</div>
<Block>
<Cell>
<Label dynamic>Umum</Label>
<Field>
<Input placeholder="Sistolik" />
</Field>
</Cell>
</Block>
<div class="my-2">
<h1 class="font-semibold">Tanda Vital</h1>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block>
<Cell>
<Label dynamic>Kesadaran (GCS)</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Pernapasan</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Jenis</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Nadi</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Jenis</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Tekanan Darah Lengan Kanan</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Tekanan Darah Lengan Kiri</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Suhu Aksila</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Suhu Rektal</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2">
<h1 class="font-semibold">Pemeriksaan Badan</h1>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block>
<Cell>
<Label dynamic>Kulit</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Kepala</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Telinga</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Hidung</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="3">
<Cell>
<Label dynamic>Rongga Mulut/Tenggorokan</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Mata</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Lain-Lain</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Leher</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Kelenjar Tiroid</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Lain-Lain</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Thorax</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Jantung</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Paru-Paru</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block>
<Cell>
<Label dynamic>Lain-Lain</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Abdomen</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Hati</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Lien</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block>
<Cell>
<Label dynamic>Lain-Lain</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<div class="my-2 rounded-md border border-slate-300 p-4">
<Block :colCount="3">
<Cell>
<Label dynamic>Punggung</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Ekstremitas</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>Kelamin</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
<Block :colCount="2">
<Cell>
<Label dynamic>Rectum</Label>
<Field>
<Input />
</Field>
</Cell>
<Cell>
<Label dynamic>System Syaraf</Label>
<Field>
<Input />
</Field>
</Cell>
</Block>
</div>
<Block :colCount="2">
<Cell>
<Label dynamic>Neuromoskuloskeletal</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Kardiorespirasi</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<Separator class="mt-8" />
<div class="my-2">
<h1 class="font-semibold">Pemeriksaan Penunjang</h1>
</div>
<Block :colCount="2">
<Cell>
<Label dynamic>Pencitraan</Label>
<Field>
<Textarea />
</Field>
</Cell>
<Cell>
<Label dynamic>Laboratorium</Label>
<Field>
<Textarea />
</Field>
</Cell>
</Block>
<div class="mb-8 grid grid-cols-2 gap-4">
<div>
<span class="text-md">Diagnosa Medis (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
</div>
<div>
<span class="text-md">Diagnosa Medis (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
</div>
<div>
<span class="text-md">Diagnosa Medis (ICD-X)</span>
<Button
class="my-2 rounded bg-orange-100 px-3 py-1 text-orange-600"
type="button"
@click="emits('click', 'prosedur')"
>
+ Pilih Prosedur
</Button>
<AppIcdPreview />
</div>
</div>
<div class="space-y-8">
<div>
<h3 class="mb-3 text-lg font-semibold">Gangguan Fungsi</h3>
<div class="grid grid-cols-3 gap-2">
<div
v-for="opt in disorderOptions"
:key="opt"
class="flex items-center space-x-2"
>
<Checkbox
:id="opt"
:value="opt"
v-model="disorders"
/>
<Label
:for="opt"
class="text-sm"
>
{{ opt }}
</Label>
</div>
</div>
<div class="mt-4 space-y-2">
<Label
for="summary"
class="font-medium"
>
Kesimpulan Didapatkan Gangguan Fungsi
<span class="text-red-500">*</span>
</Label>
<Textarea
id="summary"
v-model="summary"
placeholder="Masukkan kesimpulan gangguan fungsi"
/>
</div>
</div>
<Separator />
<!-- REKOMENDASI TERAPI -->
<div>
<h3 class="mb-3 text-lg font-semibold">Rekomendasi Terapi Tindakan Yang Diperlukan</h3>
<div class="grid grid-cols-3 gap-2">
<div
v-for="opt in therapyOptions"
:key="opt"
class="flex items-center space-x-2"
>
<Checkbox
:id="opt"
:value="opt"
v-model="therapies"
/>
<Label
:for="opt"
class="text-sm"
>
{{ opt }}
</Label>
</div>
</div>
</div>
</div>
</div>
</form>
</template>
@@ -2,7 +2,7 @@
import type { FormErrors } from '~/types/error'
import { toTypedSchema } from '@vee-validate/zod'
import Block from '~/components/pub/my-ui/form/block.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
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'
+4 -2
View File
@@ -4,15 +4,16 @@ import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Types
import type { SpecialistFormData } from '~/schemas/specialist.schema.ts'
import type { SpecialistFormData } from '~/schemas/specialist.schema'
// Helpers
import type z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { genBase } from '~/models/_base'
interface Props {
schema: z.ZodSchema<any>
@@ -59,6 +60,7 @@ const resetForm = () => {
// Form submission handler
function onSubmitForm(values: any) {
const formData: SpecialistFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
unit_id: unit.value ? Number(unit.value) : null,
@@ -4,15 +4,16 @@ import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Types
import type { SubspecialistFormData } from '~/schemas/subspecialist.schema.ts'
import type { SubspecialistFormData } from '~/schemas/subspecialist.schema'
// Helpers
import type z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { genBase } from '~/models/_base'
interface Props {
schema: z.ZodSchema<any>
@@ -60,6 +61,7 @@ const resetForm = () => {
// Form submission handler
function onSubmitForm(values: any) {
const formData: SubspecialistFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
specialist_id: specialist.value ? Number(specialist.value) : null,
+32 -6
View File
@@ -6,12 +6,13 @@ import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
// Types
import type { DeviceFormData } from '~/schemas/device.schema.ts'
import type { DeviceFormData } from '~/schemas/device.schema'
// Helpers
import type z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { genBase } from '~/models/_base'
interface Props {
schema: z.ZodSchema<any>
@@ -59,6 +60,7 @@ const resetForm = () => {
// Form submission handler
function onSubmitForm(values: any) {
const formData: DeviceFormData = {
...genBase(),
name: name.value || '',
code: code.value || '',
uom_code: uom.value || '',
@@ -73,18 +75,35 @@ function onCancelForm() {
</script>
<template>
<form id="form-tools" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-tools"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
@@ -103,7 +122,14 @@ function onCancelForm() {
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+33 -9
View File
@@ -4,10 +4,10 @@ import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Combobox from '~/components/pub/my-ui/form/combobox.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Types
import type { UnitFormData } from '~/schemas/unit.schema.ts'
import type { UnitFormData } from '~/schemas/unit.schema'
// Helpers
import type z from 'zod'
@@ -48,7 +48,7 @@ if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.installation_id !== undefined)
installation.value = props.values.installation_id ? String(props.values.installation_id) : null
installation.value = props.values.installation_id ? Number(props.values.installation_id) : null
}
const resetForm = () => {
@@ -58,7 +58,7 @@ const resetForm = () => {
}
// Form submission handler
function onSubmitForm(values: any) {
function onSubmitForm() {
const formData: UnitFormData = {
name: name.value || '',
code: code.value || '',
@@ -74,18 +74,35 @@ function onCancelForm() {
</script>
<template>
<form id="form-unit" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-unit"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
@@ -105,7 +122,14 @@ function onCancelForm() {
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+30 -7
View File
@@ -1,4 +1,3 @@
<script setup lang="ts">
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
@@ -8,7 +7,7 @@ import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
// Types
import type { UomFormData } from '~/schemas/uom.schema.ts'
import type { UomFormData } from '~/schemas/uom.schema'
// Helpers
import type z from 'zod'
@@ -65,23 +64,47 @@ function onCancelForm() {
</script>
<template>
<form id="form-medicine-method" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-medicine-method"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+56 -11
View File
@@ -5,6 +5,10 @@ import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import Button from '~/components/pub/ui/button/Button.vue'
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Types
import type { InfraFormData } from '~/schemas/infra.schema'
@@ -16,6 +20,7 @@ import { toTypedSchema } from '@vee-validate/zod'
interface Props {
schema: z.ZodSchema<any>
parents: any[]
values: any
isLoading?: boolean
isReadonly?: boolean
@@ -34,27 +39,28 @@ const { defineField, errors, meta } = useForm({
initialValues: {
code: '',
name: '',
infraGroup_code: 'counter',
infraGroup_code: infraGroupCodesKeys.warehouse,
parent_id: null,
} as Partial<InfraFormData>,
})
const [code, codeAttrs] = defineField('code')
const [name, nameAttrs] = defineField('name')
const [infraGroup_code, infraGroupAttrs] = defineField('infraGroup_code')
const [infraGroup_code] = defineField('infraGroup_code')
const [parent_id, parentIdAttrs] = defineField('parent_id')
if (props.values) {
if (props.values.code !== undefined) code.value = props.values.code
if (props.values.name !== undefined) name.value = props.values.name
if (props.values.infraGroup_code !== undefined) infraGroup_code.value = props.values.infraGroup_code
if (props.values.parent_id !== undefined) parent_id.value = props.values.parent_id
if (props.values.parent_id !== undefined)
parent_id.value = props.values.parent_id ? Number(props.values.parent_id) : null
}
const resetForm = () => {
code.value = ''
name.value = ''
infraGroup_code.value = 'counter'
infraGroup_code.value = infraGroupCodesKeys.warehouse
parent_id.value = null
}
@@ -62,8 +68,8 @@ function onSubmitForm() {
const formData: InfraFormData = {
code: code.value || '',
name: name.value || '',
infraGroup_code: infraGroup_code.value || 'counter',
parent_id: parent_id.value || null,
infraGroup_code: infraGroup_code.value || infraGroupCodesKeys.warehouse,
parent_id: parent_id.value ? Number(parent_id.value) : null,
}
emit('submit', formData, resetForm)
}
@@ -74,23 +80,62 @@ function onCancelForm() {
</script>
<template>
<form id="form-counter" @submit.prevent>
<Block labelSize="thin" class="!mb-2.5 !pt-0 xl:!mb-3" :colCount="1">
<form
id="form-floor"
@submit.prevent
>
<Block
labelSize="thin"
class="!mb-2.5 !pt-0 xl:!mb-3"
:colCount="1"
>
<Cell>
<Label height="compact">Kode</Label>
<Field :errMessage="errors.code">
<Input id="code" v-model="code" v-bind="codeAttrs" :disabled="isLoading || isReadonly" />
<Input
id="code"
v-model="code"
v-bind="codeAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Nama</Label>
<Field :errMessage="errors.name">
<Input id="name" v-model="name" v-bind="nameAttrs" :disabled="isLoading || isReadonly" />
<Input
id="name"
v-model="name"
v-bind="nameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
</Cell>
<Cell>
<Label height="compact">Lantai</Label>
<Field :errMessage="errors.parent_id">
<Combobox
id="parent"
v-model="parent_id"
v-bind="parentIdAttrs"
:items="parents"
:is-disabled="isLoading || isReadonly"
placeholder="Pilih Lantai"
search-placeholder="Cari Lantai"
empty-message="Item tidak ditemukan"
/>
</Field>
</Cell>
</Block>
<div class="my-2 flex justify-end gap-2 py-2">
<Button type="button" variant="secondary" class="w-[120px]" @click="onCancelForm"> Kembali </Button>
<Button
type="button"
variant="secondary"
class="w-[120px]"
@click="onCancelForm"
>
Kembali
</Button>
<Button
v-if="!isReadonly"
type="button"
+2 -5
View File
@@ -12,9 +12,9 @@ type SmallDetailDto = any
const action = defineAsyncComponent(() => import('~/components/pub/my-ui/data/dropdown-action-dud.vue'))
export const cols: Col[] = [{ width: 100 }, {}, {}, { width: 50 }]
export const cols: Col[] = [{}, {}, {}, { width: 50 }]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Counter Induk' }, { label: '' }]]
export const header: Th[][] = [[{ label: 'Kode' }, { label: 'Nama' }, { label: 'Lantai' }, { label: '' }]]
export const keys = ['code', 'name', 'parent', 'action']
@@ -36,9 +36,6 @@ export const funcComponent: RecStrFuncComponent = {
idx,
rec: rec as object,
component: action,
props: {
size: 'sm',
},
}
return res
},
+7 -2
View File
@@ -1,6 +1,11 @@
<script setup lang="ts">
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Components
import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Configs
import { cols, funcComponent, funcHtml, funcParsed, header, keys } from './list-cfg'
interface Props {
@@ -21,7 +26,7 @@ function handlePageChange(page: number) {
<template>
<div class="space-y-4">
<PubBaseDataTable
<PubMyUiDataTable
:rows="data"
:cols="cols"
:header="header"
+196 -6
View File
@@ -1,8 +1,198 @@
<!-- Duplicated from content/counter/list.vue for bed -->
<!-- TODO: Update logic and fields for bed context -->
<template>
...existing code...
</template>
<script setup lang="ts">
// ...existing code...
// Components
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import AppBedList from '~/components/app/bed/list.vue'
import AppBedEntryForm from '~/components/app/bed/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { InfraSchema, type InfraFormData } from '~/schemas/infra.schema'
// Handlers
import {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/infra.handler'
// Services
import { getList, getDetail, getValueLabelList } from '~/services/infra.service'
const parents = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
data,
isLoading,
paginationMeta,
searchInput,
handlePageChange,
handleSearch,
fetchData: getItemList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 2,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.bed,
includes: 'parent',
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'bed',
})
const headerPrep: HeaderPrep = {
title: 'Kasur',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
onInput: (val: string) => {
searchInput.value = val
},
onClick: () => {},
onClear: () => {},
},
addNav: {
label: 'Tambah',
icon: 'i-lucide-plus',
onClick: () => {
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = true
isReadonly.value = false
},
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
}
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentDetail(recId.value)
title.value = 'Detail Kasur'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentDetail(recId.value)
title.value = 'Edit Kasur'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
onMounted(async () => {
parents.value = await getValueLabelList({ sort: 'createdAt:asc', 'infraGroup-code': infraGroupCodesKeys.chamber })
await getItemList()
})
</script>
<template>
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppBedList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Kasur'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppBedEntryForm
:schema="InfraSchema"
:parents="parents"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: InfraFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getItemList, resetForm, toast)
return
}
handleActionSave(values, getItemList, resetForm, toast)
}
"
@cancel="handleCancelForm"
/>
</Dialog>
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getItemList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
</template>
+40 -8
View File
@@ -7,7 +7,7 @@ import AppBuildingEntryForm from '~/components/app/building/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from "~/lib/constants"
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
@@ -26,6 +26,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -49,6 +50,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.building,
@@ -62,7 +64,7 @@ const headerPrep: HeaderPrep = {
title: 'Gedung',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari gedung...',
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
@@ -122,10 +124,31 @@ onMounted(async () => {
</script>
<template>
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<AppBuildingList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppBuildingList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Gedung'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Gedung'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppBuildingEntryForm
:schema="InfraSchema"
:values="recItem"
@@ -153,9 +176,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+196 -6
View File
@@ -1,8 +1,198 @@
<!-- Duplicated from content/counter/list.vue for chamber -->
<!-- TODO: Update logic and fields for chamber context -->
<template>
...existing code...
</template>
<script setup lang="ts">
// ...existing code...
// Components
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import AppChamberList from '~/components/app/chamber/list.vue'
import AppChamberEntryForm from '~/components/app/chamber/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { InfraSchema, type InfraFormData } from '~/schemas/infra.schema'
// Handlers
import {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/infra.handler'
// Services
import { getList, getDetail, getValueLabelList } from '~/services/infra.service'
const parents = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
data,
isLoading,
paginationMeta,
searchInput,
handlePageChange,
handleSearch,
fetchData: getItemList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 2,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.chamber,
includes: 'parent',
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'chamber',
})
const headerPrep: HeaderPrep = {
title: 'Kamar',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
onInput: (val: string) => {
searchInput.value = val
},
onClick: () => {},
onClear: () => {},
},
addNav: {
label: 'Tambah',
icon: 'i-lucide-plus',
onClick: () => {
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = true
isReadonly.value = false
},
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
}
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentDetail(recId.value)
title.value = 'Detail Kamar'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentDetail(recId.value)
title.value = 'Edit Kamar'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
onMounted(async () => {
parents.value = await getValueLabelList({ sort: 'createdAt:asc', 'infraGroup-code': infraGroupCodesKeys.floor })
await getItemList()
})
</script>
<template>
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppChamberList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Kamar'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppChamberEntryForm
:schema="InfraSchema"
:parents="parents"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: InfraFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getItemList, resetForm, toast)
return
}
handleActionSave(values, getItemList, resetForm, toast)
}
"
@cancel="handleCancelForm"
/>
</Dialog>
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getItemList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
</template>
+61 -19
View File
@@ -2,9 +2,13 @@
// Components
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import AppCounterList from '~/components/app/counter/list.vue'
import AppCounterEntryForm from '~/components/app/counter/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
@@ -22,6 +26,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -29,9 +34,10 @@ import {
} from '~/handlers/infra.handler'
// Services
import { getInfras, getInfraDetail } from '~/services/infra.service'
import { getList, getDetail } from '~/services/infra.service'
const title = ref('')
const {
data,
isLoading,
@@ -39,10 +45,16 @@ const {
searchInput,
handlePageChange,
handleSearch,
fetchData: getCounterList,
fetchData: getItemList,
} = usePaginatedList({
fetchFn: async ({ page, search }) => {
const result = await getInfras({ search, page, infraGroup_code: 'counter' })
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.counter,
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'counter',
@@ -52,7 +64,7 @@ const headerPrep: HeaderPrep = {
title: 'Counter',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari counter...',
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
@@ -79,8 +91,8 @@ provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentCounterDetail = async (id: number | string) => {
const result = await getInfraDetail(id)
const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
@@ -91,12 +103,12 @@ const getCurrentCounterDetail = async (id: number | string) => {
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentCounterDetail(recId.value)
getCurrentDetail(recId.value)
title.value = 'Detail Counter'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentCounterDetail(recId.value)
getCurrentDetail(recId.value)
title.value = 'Edit Counter'
isReadonly.value = false
break
@@ -107,15 +119,36 @@ watch([recId, recAction], () => {
})
onMounted(async () => {
await getCounterList()
await getItemList()
})
</script>
<template>
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<AppCounterList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppCounterList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Counter'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Counter'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppCounterEntryForm
:schema="InfraSchema"
:values="recItem"
@@ -124,10 +157,10 @@ onMounted(async () => {
@submit="
(values: InfraFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getCounterList, resetForm, toast)
handleActionEdit(recId, values, getItemList, resetForm, toast)
return
}
handleActionSave(values, getCounterList, resetForm, toast)
handleActionSave(values, getItemList, resetForm, toast)
}
"
@cancel="handleCancelForm"
@@ -138,14 +171,23 @@ onMounted(async () => {
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getCounterList, toast)"
@confirm="() => handleActionRemove(recId, getItemList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+3 -3
View File
@@ -139,11 +139,11 @@ onMounted(() => {
.join('')
}}</AvatarFallback>
</Avatar>
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">
<div class="grid gap-1 min-w-0">
<p class="text-sm font-medium leading-none truncate">
{{ recentSales.name }}
</p>
<p class="text-sm text-muted-foreground">
<p class="text-sm text-muted-foreground truncate">
{{ recentSales.email }}
</p>
</div>
@@ -0,0 +1,201 @@
<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 AppDivisionPositionList from '~/components/app/division-position/list.vue'
import AppDivisionPositionEntryForm from '~/components/app/division-position/entry-form.vue'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { DivisionPositionSchema, type DivisionPositionFormData } from '~/schemas/division-position.schema'
// Handlers
import {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/division-position.handler'
// Services
import { getList, getDetail } from '~/services/division-position.service'
import { getValueLabelList as getDivisionLabelList } from '~/services/division.service'
import { getValueLabelList as getEmployeeLabelList } from '~/services/employee.service'
const divisions = ref<{ value: string | number; label: string }[]>([])
const employees = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
data,
isLoading,
paginationMeta,
searchInput,
handlePageChange,
handleSearch,
fetchData: getDivisionList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'division',
})
const headerPrep: HeaderPrep = {
title: 'Divisi',
icon: 'i-lucide-box',
refSearchNav: {
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
onInput: (value: string) => {
searchInput.value = value
},
onClick: () => {},
onClear: () => {},
},
addNav: {
label: 'Tambah',
icon: 'i-lucide-plus',
onClick: () => {
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = true
isReadonly.value = false
},
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentDivisionDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
}
// Watch for row actions when recId or recAction changes
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentDivisionDetail(recId.value)
title.value = 'Detail Divisi'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentDivisionDetail(recId.value)
title.value = 'Edit Divisi'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
onMounted(async () => {
divisions.value = await getDivisionLabelList({ sort: 'createdAt:asc', 'page-size': 100 })
employees.value = await getEmployeeLabelList({ sort: 'createdAt:asc', 'page-size': 100 })
await getDivisionList()
})
</script>
<template>
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppDivisionPositionList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Divisi'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppDivisionPositionEntryForm
:schema="DivisionPositionSchema"
:divisions="divisions"
:employees="employees"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: DivisionPositionFormData | Record<string, any>, resetForm: () => void) => {
console.log(values)
if (recId > 0) {
handleActionEdit(recId, values, getDivisionList, resetForm, toast)
return
}
handleActionSave(values, getDivisionList, resetForm, toast)
}
"
@cancel="handleCancelForm"
/>
</Dialog>
<!-- Record Confirmation Modal -->
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getDivisionList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
</template>
+11 -1
View File
@@ -13,6 +13,7 @@ import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { DivisionSchema, type DivisionFormData } from '~/schemas/division.schema'
import type { Division } from "~/models/division"
import type { TreeItem } from '~/models/_base'
// Handlers
@@ -24,6 +25,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -48,6 +50,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'parent,childrens',
@@ -127,7 +130,8 @@ watch(
})
if (result.success) {
const currentData = result.body.data || []
divisionsTrees.value = getValueTreeItems(currentData || [])
const normalizedData = currentData.filter((division: Division) => !division.parent_id)
divisionsTrees.value = getValueTreeItems(normalizedData)
}
},
)
@@ -156,6 +160,12 @@ onMounted(async () => {
:title="!!recItem ? title : 'Tambah Divisi'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppDivisionEntryForm
:schema="DivisionSchema"
+1 -1
View File
@@ -49,7 +49,7 @@ const refSearchNav: RefSearchNav = {
async function getPatientList() {
isLoading.isTableLoading = true
const resp = await xfetch('/api/v1/patient')
const resp = await xfetch('/api/v1/encounter')
if (resp.success) {
data.value = (resp.body as Record<string, any>).data
}
+21 -4
View File
@@ -7,9 +7,15 @@ import { useRoute, useRouter } from 'vue-router'
import CompTab from '~/components/pub/my-ui/comp-tab/comp-tab.vue'
import type { TabItem } from '~/components/pub/my-ui/comp-tab/type'
import Status from '~/components/app/encounter/status.vue'
import { getDetail } from '~/services/encounter.service'
import AssesmentFunctionList from '~/components/content/assesment-function/list.vue'
import EarlyMedicalAssesmentList from '~/components/content/soapi/entry.vue'
import PrescriptionList from '~/components/content/prescription/list.vue'
import type { Encounter } from '~/models/encounter'
import Status from '~/components/app/encounter/status.vue'
// import AssesmentFunctionList from '~/components/content/assesment-function/list.vue'
import EarlyMedicalRehabList from '~/components/content/soapi/entry.vue'
const route = useRoute()
const router = useRouter()
@@ -22,6 +28,9 @@ const activeTab = computed({
},
})
const id = typeof route.params.id == 'string' ? parseInt(route.params.id) : 0
const encounter = ref<Encounter>((await getDetail(id)) as Encounter)
const data = {
noRm: 'RM21123',
nama: 'Ahmad Sutanto',
@@ -38,13 +47,17 @@ const data = {
const tabs: TabItem[] = [
{ value: 'status', label: 'Status Masuk/Keluar', component: Status },
{ value: 'early-medical-assessment', label: 'Pengkajian Awal Medis', component: EarlyMedicalAssesmentList },
{ value: 'rehab-medical-assessment', label: 'Pengkajian Awal Medis Rehabilitasi Medis' },
{
value: 'rehab-medical-assessment',
label: 'Pengkajian Awal Medis Rehabilitasi Medis',
component: EarlyMedicalRehabList,
},
{ value: 'function-assessment', label: 'Asesmen Fungsi', component: AssesmentFunctionList },
{ value: 'therapy-protocol', label: 'Protokol Terapi' },
{ value: 'education-assessment', label: 'Asesmen Kebutuhan Edukasi' },
{ value: 'consent', label: 'General Consent' },
{ value: 'patient-note', label: 'CPRJ' },
{ value: 'prescription', label: 'Order Obat' },
{ value: 'prescription', label: 'Order Obat', component: PrescriptionList },
{ value: 'device', label: 'Order Alkes' },
{ value: 'mcu-radiology', label: 'Order Radiologi' },
{ value: 'mcu-lab-pc', label: 'Order Lab PK' },
@@ -66,6 +79,10 @@ const tabs: TabItem[] = [
<PubMyUiNavContentBa label="Kembali ke Daftar Kunjungan" />
</div>
<AppEncounterQuickInfo :data="data" />
<CompTab :data="tabs" :initial-active-tab="activeTab" @change-tab="activeTab = $event" />
<CompTab
:data="tabs"
:initial-active-tab="activeTab"
@change-tab="activeTab = $event"
/>
</div>
</template>
+26 -5
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -33,7 +34,7 @@ import {
import { getList, getDetail } from '~/services/material.service'
import { getValueLabelList as getUomList } from '~/services/uom.service'
const uoms = ref<{ value: string; label: string }[]>([])
const uoms = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
@@ -48,6 +49,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
@@ -129,13 +131,23 @@ onMounted(async () => {
:ref-search-nav="headerPrep.refSearchNav"
class="mb-4 xl:mb-5"
/>
<AppEquipmentList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppEquipmentList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Perlengkapan'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppEquipmentEntryForm
:schema="MaterialSchema"
@@ -166,9 +178,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+41 -9
View File
@@ -26,6 +26,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -35,7 +36,7 @@ import {
// Services
import { getList, getDetail, getValueLabelList } from '~/services/infra.service'
const parents = ref<{ value: string; label: string }[]>([])
const parents = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
@@ -50,6 +51,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 2,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.floor,
@@ -64,7 +66,7 @@ const headerPrep: HeaderPrep = {
title: 'Lantai',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari lantai...',
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
@@ -119,16 +121,37 @@ watch([recId, recAction], () => {
})
onMounted(async () => {
parents.value = await getValueLabelList({ 'infraGroup-code': infraGroupCodesKeys.building })
parents.value = await getValueLabelList({ sort: 'createdAt:asc', 'infraGroup-code': infraGroupCodesKeys.building })
await getItemList()
})
</script>
<template>
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<AppFloorList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppFloorList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Gedung'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Lantai'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppFloorEntryForm
:schema="InfraSchema"
:parents="parents"
@@ -157,9 +180,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+26 -5
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -48,7 +49,8 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
'page-number': params['page-number'] || 0,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
return { success: result.success || false, body: result.body || {} }
@@ -129,13 +131,23 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppInstallationList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppInstallationList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Instalasi'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppInstallationEntryForm
:schema="InstallationSchema"
@@ -166,9 +178,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+26 -5
View File
@@ -12,7 +12,7 @@ import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { BaseSchema, type BaseFormData } from '~/schemas/my-ui.schema'
import { BaseSchema, type BaseFormData } from '~/schemas/base.schema'
// Handlers
import {
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -46,6 +47,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
@@ -126,13 +128,23 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppMedicineGroupList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppMedicineGroupList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Kelompok Obat'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppMedicineGroupEntryForm
:schema="BaseSchema"
@@ -162,9 +174,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
@@ -12,7 +12,7 @@ import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { BaseSchema, type BaseFormData } from '~/schemas/my-ui.schema'
import { BaseSchema, type BaseFormData } from '~/schemas/base.schema'
// Handlers
import {
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -46,6 +47,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
@@ -126,13 +128,23 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppMedicineMethodList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppMedicineMethodList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Metode Obat'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppMedicineMethodEntryForm
:schema="BaseSchema"
@@ -162,9 +174,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+34 -8
View File
@@ -26,6 +26,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -55,6 +56,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'medicineGroup,medicineMethod,uom',
@@ -123,9 +125,9 @@ watch([recId, recAction], () => {
})
onMounted(async () => {
medicineGroups.value = await getMedicineGroupList()
medicineMethods.value = await getMedicineMethodList()
uoms.value = await getUomList()
medicineGroups.value = await getMedicineGroupList({ sort: 'createdAt:asc', 'page-size': 100 })
medicineMethods.value = await getMedicineMethodList({ sort: 'createdAt:asc', 'page-size': 100 })
uoms.value = await getUomList({ sort: 'createdAt:asc', 'page-size': 100 })
await getMedicineList()
})
</script>
@@ -138,9 +140,24 @@ onMounted(async () => {
:ref-search-nav="headerPrep.refSearchNav"
class="mb-4 xl:mb-5"
/>
<AppMedicineList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppMedicineList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Obat'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Obat'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppMedicineEntryForm
:schema="MedicineSchema"
:values="recItem"
@@ -172,9 +189,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
@@ -0,0 +1,194 @@
<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 AppPublicScreenList from '~/components/app/public-screen/list.vue'
import AppPublicScreenEntryForm from '~/components/app/public-screen/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { InfraSchema, type InfraFormData } from '~/schemas/infra.schema'
// Handlers
import {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/infra.handler'
// Services
import { getList, getDetail } from '~/services/infra.service'
const title = ref('')
const {
data,
isLoading,
paginationMeta,
searchInput,
handlePageChange,
handleSearch,
fetchData: getItemList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys['public-screen'],
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'counter',
})
const headerPrep: HeaderPrep = {
title: 'Layar Publik',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
onInput: (val: string) => {
searchInput.value = val
},
onClick: () => {},
onClear: () => {},
},
addNav: {
label: 'Tambah',
icon: 'i-lucide-plus',
onClick: () => {
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = true
isReadonly.value = false
},
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
}
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentDetail(recId.value)
title.value = 'Detail Layar Publik'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentDetail(recId.value)
title.value = 'Edit Layar Publik'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
onMounted(async () => {
await getItemList()
})
</script>
<template>
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppPublicScreenList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Layar Publik'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppPublicScreenEntryForm
:schema="InfraSchema"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: InfraFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getItemList, resetForm, toast)
return
}
handleActionSave(values, getItemList, resetForm, toast)
}
"
@cancel="handleCancelForm"
/>
</Dialog>
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getItemList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
</template>
+92 -25
View File
@@ -26,6 +26,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -37,11 +38,14 @@ import { getList, getDetail, getValueLabelList } from '~/services/infra.service'
import { getValueLabelList as getSpecialistList } from '~/services/specialist.service'
import { getValueLabelList as getSubspecialistList } from '~/services/subspecialist.service'
import { getValueLabelList as getUnitList } from '~/services/unit.service'
import { getDetail as getItemDetail } from '~/services/item.service'
const parents = ref<{ value: string | number; label: string }[]>([])
const specialists = ref<{ value: string; label: string }[]>([])
const subspecialists = ref<{ value: string; label: string }[]>([])
const units = ref<{ value: string; label: string }[]>([])
const specialists = ref<{ value: string | number; label: string }[]>([])
const specialistsFiltered = ref<{ value: string | number; label: string }[]>([])
const subspecialists = ref<{ value: string | number; label: string }[]>([])
const subspecialistsFiltered = ref<{ value: string | number; label: string }[]>([])
const units = ref<{ value: string | number; label: string }[]>([])
const selectedUnit = ref<string | number | null>(null)
const selectedSpecialist = ref<string | number | null>(null)
const title = ref('')
@@ -58,10 +62,11 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 2,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.room,
includes: 'parent,specialist,subspecialist,unit',
includes: 'parent',
})
return { success: result.success || false, body: result.body || {} }
},
@@ -72,7 +77,7 @@ const headerPrep: HeaderPrep = {
title: 'Ruangan',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari Ruangan...',
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
@@ -103,6 +108,34 @@ const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
if (currentValue.item_id) {
const itemResult = await getItemDetail(currentValue.item_id)
if (itemResult.success) {
currentValue.item = itemResult.body?.data || {}
}
}
if (currentValue.rooms) {
const rooms: any = Array.isArray(currentValue.rooms) && currentValue.rooms.length > 0 ? currentValue.rooms[0] : {}
specialistsFiltered.value = rooms?.specialist
? [
{
value: rooms.specialist?.id ? Number(rooms.specialist.id) : '',
label: rooms.specialist?.name || '',
},
]
: []
subspecialistsFiltered.value = rooms?.subspecialist
? [
{
value: rooms.subspecialist?.id ? Number(rooms.subspecialist.id) : '',
label: rooms.subspecialist?.name || '',
},
]
: []
currentValue.unit_id = rooms?.unit_id || null
currentValue.specialist_id = rooms?.specialist_id || null
currentValue.subspecialist_id = rooms?.subspecialist_id || null
}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
@@ -126,42 +159,67 @@ watch([recId, recAction], () => {
}
})
watch(selectedUnit, async (val) => {
if (val) {
specialists.value = await getSpecialistList({ 'unit-id': val, 'page-size': 100 })
watch(selectedUnit, async (value: string | number | null) => {
specialistsFiltered.value = []
if (value) {
selectedSpecialist.value = null
subspecialists.value = []
specialistsFiltered.value = specialists.value.filter((item: any) => Number(item.parent) === Number(value))
subspecialistsFiltered.value = []
} else {
specialists.value = []
selectedSpecialist.value = null
subspecialists.value = []
specialistsFiltered.value = []
subspecialistsFiltered.value = []
}
})
watch(selectedSpecialist, async (val) => {
if (val) {
subspecialists.value = await getSubspecialistList({ 'specialist-id': val, 'page-size': 100 })
watch(selectedSpecialist, async (value: string | number | null) => {
subspecialistsFiltered.value = []
if (value) {
subspecialistsFiltered.value = subspecialists.value.filter((item: any) => Number(item.parent) === Number(value))
} else {
subspecialists.value = []
subspecialistsFiltered.value = []
}
})
onMounted(async () => {
parents.value = await getValueLabelList({ 'infraGroup-code': infraGroupCodesKeys.floor })
units.value = await getUnitList({ 'page-size': 100 })
parents.value = await getValueLabelList({ sort: 'createdAt:asc', 'infraGroup-code': infraGroupCodesKeys.floor })
specialists.value = await getSpecialistList({ sort: 'createdAt:asc', 'page-size': 100 })
subspecialists.value = await getSubspecialistList({ sort: 'createdAt:asc', 'page-size': 100 })
units.value = await getUnitList({ sort: 'createdAt:asc', 'page-size': 100 })
await getItemList()
})
</script>
<template>
<Header v-model="searchInput" :prep="headerPrep" @search="handleSearch" class="mb-4 xl:mb-5" />
<AppRoomList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppRoomList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Ruangan'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Ruangan'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppRoomEntryForm
:schema="InfraSchema"
:specialists="specialists"
:subspecialists="subspecialists"
:specialists="specialistsFiltered"
:subspecialists="subspecialistsFiltered"
:units="units"
:parents="parents"
:values="recItem"
@@ -191,9 +249,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+27 -5
View File
@@ -1,16 +1,38 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useRoute } from 'vue-router'
import { useQueryMode } from '@/composables/useQueryMode' // asumsikan ini ada
import EarlyMedicalAssesmentList from './list.vue'
import EarlyMedicalAssesmentForm from './form.vue'
import SoapiList from './list.vue'
import EarlyForm from './form.vue'
import RehabForm from './form-rehab.vue'
import FunctionForm from './form-function.vue'
const route = useRoute()
const type = computed(() => (route.query.tab as string) || 'early-medical-assessment')
const { mode, openForm, backToList } = useQueryMode('mode')
const formMap = {
'early-medical-assessment': EarlyForm,
'rehab-medical-assessment': RehabForm,
'function-assessment': FunctionForm,
}
const ActiveForm = computed(() => formMap[type.value] || EarlyForm)
</script>
<template>
<div>
<EarlyMedicalAssesmentList v-if="mode === 'list'" @add="openForm" />
<EarlyMedicalAssesmentForm v-else @back="backToList" />
<SoapiList
v-if="mode === 'list'"
@add="openForm"
/>
<component
v-else
:is="ActiveForm"
@back="backToList"
/>
</div>
</template>
@@ -0,0 +1,45 @@
<script setup lang="ts">
import Entry from '~/components/app/soapi/entry.vue'
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
const isOpen = ref(false)
const data = ref([])
const isLoading = reactive<DataTableLoader>({
isTableLoading: false,
})
async function getPatientList() {
isLoading.isTableLoading = true
const resp = await xfetch('/api/v1/patient')
if (resp.success) {
data.value = (resp.body as Record<string, any>).data
}
isLoading.isTableLoading = false
}
onMounted(() => {
getPatientList()
})
function handleClick(type: string) {
console.log(type)
isOpen.value = true
}
provide('table_data_loader', isLoading)
</script>
<template>
<Entry
type="function"
:exclude-fields="['prim-compl', 'sec-compl']"
@click="handleClick"
/>
<Dialog
v-model:open="isOpen"
title="Pilih Prosedur"
size="xl"
prevent-outside
>
<AppIcdMultiselectPicker :data="data" />
</Dialog>
</template>
@@ -0,0 +1,45 @@
<script setup lang="ts">
import Entry from '~/components/app/soapi/entry.vue'
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
const isOpen = ref(false)
const data = ref([])
const isLoading = reactive<DataTableLoader>({
isTableLoading: false,
})
async function getPatientList() {
isLoading.isTableLoading = true
const resp = await xfetch('/api/v1/patient')
if (resp.success) {
data.value = (resp.body as Record<string, any>).data
}
isLoading.isTableLoading = false
}
onMounted(() => {
getPatientList()
})
function handleClick(type: string) {
console.log(type)
isOpen.value = true
}
provide('table_data_loader', isLoading)
</script>
<template>
<Entry
type="early-rehab"
:exclude-fields="['prim-compl', 'sec-compl']"
@click="handleClick"
/>
<Dialog
v-model:open="isOpen"
title="Pilih Prosedur"
size="xl"
prevent-outside
>
<AppIcdMultiselectPicker :data="data" />
</Dialog>
</template>
+25 -4
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -48,6 +49,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'unit',
@@ -130,13 +132,23 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppSpecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppSpecialistList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Spesialis'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppSpecialistEntryForm
:schema="SpecialistSchema"
@@ -167,9 +179,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+25 -4
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -48,6 +49,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'specialist',
@@ -130,13 +132,23 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppSubSpecialistList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppSubSpecialistList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Sub Spesialis'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppSubSpecialistEntryForm
:schema="SubspecialistSchema"
@@ -167,9 +179,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+30 -5
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -46,7 +47,12 @@ const {
fetchData: getToolsList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({ search: params.search, 'page-number': params['page-number'] || 0 })
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'device',
@@ -130,13 +136,23 @@ onMounted(async () => {
:ref-search-nav="headerPrep.refSearchNav"
class="mb-4 xl:mb-5"
/>
<AppToolsList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppToolsList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Peralatan'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppToolsEntryForm
:schema="DeviceSchema"
@@ -167,9 +183,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+31 -5
View File
@@ -23,6 +23,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -48,6 +49,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
includes: 'installation',
@@ -130,9 +132,24 @@ onMounted(async () => {
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppUnitList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppUnitList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Unit'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Unit'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppUnitEntryForm
:schema="UnitSchema"
:installations="installations"
@@ -162,9 +179,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+31 -5
View File
@@ -22,6 +22,7 @@ import {
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
@@ -45,6 +46,7 @@ const {
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 0,
'page-size': params['page-size'] || 10,
})
@@ -127,10 +129,25 @@ onMounted(async () => {
class="mb-4 xl:mb-5"
/>
<div class="rounded-md border p-4">
<AppUomList :data="data" :pagination-meta="paginationMeta" @page-change="handlePageChange" />
<AppUomList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
</div>
<Dialog v-model:open="isFormEntryDialogOpen" :title="!!recItem ? title : 'Tambah Uom'" size="lg" prevent-outside>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Uom'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppUomEntryForm
:schema="UomSchema"
:values="recItem"
@@ -159,9 +176,18 @@ onMounted(async () => {
>
<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>
<p v-if="record?.code"><strong>Kode:</strong> {{ record.code }}</p>
<p>
<strong>ID:</strong>
{{ record?.id }}
</p>
<p v-if="record?.name">
<strong>Nama:</strong>
{{ record.name }}
</p>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
+196 -6
View File
@@ -1,8 +1,198 @@
<!-- Duplicated from content/counter/list.vue for warehouse -->
<!-- TODO: Update logic and fields for warehouse context -->
<template>
...existing code...
</template>
<script setup lang="ts">
// ...existing code...
// Components
import Dialog from '~/components/pub/my-ui/modal/dialog.vue'
import Header from '~/components/pub/my-ui/nav-header/prep.vue'
import AppWarehouseList from '~/components/app/warehouse/list.vue'
import AppWarehouseEntryForm from '~/components/app/warehouse/entry-form.vue'
import RecordConfirmation from '~/components/pub/my-ui/confirmation/record-confirmation.vue'
// Constants
import { infraGroupCodesKeys } from '~/lib/constants'
// Helpers
import { usePaginatedList } from '~/composables/usePaginatedList'
import { toast } from '~/components/pub/ui/toast'
// Types
import { ActionEvents, type HeaderPrep } from '~/components/pub/my-ui/data/types'
import { InfraSchema, type InfraFormData } from '~/schemas/infra.schema'
// Handlers
import {
recId,
recAction,
recItem,
isReadonly,
isProcessing,
isFormEntryDialogOpen,
isRecordConfirmationOpen,
onResetState,
handleActionSave,
handleActionEdit,
handleActionRemove,
handleCancelForm,
} from '~/handlers/infra.handler'
// Services
import { getList, getDetail, getValueLabelList } from '~/services/infra.service'
const parents = ref<{ value: string | number; label: string }[]>([])
const title = ref('')
const {
data,
isLoading,
paginationMeta,
searchInput,
handlePageChange,
handleSearch,
fetchData: getItemList,
} = usePaginatedList({
fetchFn: async (params: any) => {
const result = await getList({
search: params.search,
sort: 'createdAt:asc',
'page-number': params['page-number'] || 2,
'page-size': params['page-size'] || 10,
'infraGroup-code': infraGroupCodesKeys.warehouse,
includes: 'parent',
})
return { success: result.success || false, body: result.body || {} }
},
entityName: 'warehouse',
})
const headerPrep: HeaderPrep = {
title: 'Gudang',
icon: 'i-lucide-layout-list',
refSearchNav: {
placeholder: 'Cari (min. 3 karakter)...',
minLength: 3,
debounceMs: 500,
showValidationFeedback: true,
onInput: (val: string) => {
searchInput.value = val
},
onClick: () => {},
onClear: () => {},
},
addNav: {
label: 'Tambah',
icon: 'i-lucide-plus',
onClick: () => {
recItem.value = null
recId.value = 0
isFormEntryDialogOpen.value = true
isReadonly.value = false
},
},
}
provide('rec_id', recId)
provide('rec_action', recAction)
provide('rec_item', recItem)
provide('table_data_loader', isLoading)
const getCurrentDetail = async (id: number | string) => {
const result = await getDetail(id)
if (result.success) {
const currentValue = result.body?.data || {}
recItem.value = currentValue
isFormEntryDialogOpen.value = true
}
}
watch([recId, recAction], () => {
switch (recAction.value) {
case ActionEvents.showDetail:
getCurrentDetail(recId.value)
title.value = 'Detail Gudang'
isReadonly.value = true
break
case ActionEvents.showEdit:
getCurrentDetail(recId.value)
title.value = 'Edit Gudang'
isReadonly.value = false
break
case ActionEvents.showConfirmDelete:
isRecordConfirmationOpen.value = true
break
}
})
onMounted(async () => {
parents.value = await getValueLabelList({ sort: 'createdAt:asc', 'infraGroup-code': infraGroupCodesKeys.floor })
await getItemList()
})
</script>
<template>
<Header
v-model="searchInput"
:prep="headerPrep"
:ref-search-nav="headerPrep.refSearchNav"
@search="handleSearch"
class="mb-4 xl:mb-5"
/>
<AppWarehouseList
:data="data"
:pagination-meta="paginationMeta"
@page-change="handlePageChange"
/>
<Dialog
v-model:open="isFormEntryDialogOpen"
:title="!!recItem ? title : 'Tambah Gudang'"
size="lg"
prevent-outside
@update:open="
(value: any) => {
onResetState()
isFormEntryDialogOpen = value
}
"
>
<AppWarehouseEntryForm
:schema="InfraSchema"
:parents="parents"
:values="recItem"
:is-loading="isProcessing"
:is-readonly="isReadonly"
@submit="
(values: InfraFormData | Record<string, any>, resetForm: () => void) => {
if (recId > 0) {
handleActionEdit(recId, values, getItemList, resetForm, toast)
return
}
handleActionSave(values, getItemList, resetForm, toast)
}
"
@cancel="handleCancelForm"
/>
</Dialog>
<RecordConfirmation
v-model:open="isRecordConfirmationOpen"
action="delete"
:record="recItem"
@confirm="() => handleActionRemove(recId, getItemList, toast)"
@cancel=""
>
<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>
<p v-if="record?.code">
<strong>Kode:</strong>
{{ record.code }}
</p>
</div>
</template>
</RecordConfirmation>
</template>
+2 -2
View File
@@ -22,7 +22,7 @@ const openCollapsible = ref(false)
<Collapsible :key="item.title" v-model:open="openCollapsible" as-child class="group/collapsible ">
<SidebarMenuItem>
<CollapsibleTrigger as-child>
<SidebarMenuButton :tooltip="item.title" :size="size" class="md:!text-xs xl:!text-sm 2xl:!text-base">
<SidebarMenuButton :tooltip="item.title" :size="size" class="md:!text-xs 2xl:!text-sm">
<Icon :name="item.icon || ''" mode="svg" />
<span class="mx-2">{{ item.title }}</span>
<Icon
@@ -37,7 +37,7 @@ const openCollapsible = ref(false)
<SidebarMenuSubButton as-child>
<NuxtLink
:to="subItem.link"
class="mx-4 rounded-lg py-5 text-sm transition-all duration-200 md:!text-xs xl:!text-sm 2xl:!text-base"
class="mx-4 rounded-lg py-2 2xl:py-2.5 text-sm transition-all duration-200 md:!text-xs 2xl:!text-sm 2xl:!text-base"
active-class="bg-primary text-white"
@click="setOpenMobile(false)"
>
+3 -3
View File
@@ -22,13 +22,13 @@ const activeTeam = ref(props.teams[0])
<div
class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg"
>
<img :src="activeTeam.logo" class="h-full w-full" />
<img :src="activeTeam?.logo" class="h-full w-full" />
</div>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">
{{ activeTeam.name }}
{{ activeTeam?.name }}
</span>
<span class="truncate text-xs">{{ activeTeam.plan }}</span>
<span class="truncate text-xs">{{ activeTeam?.plan }}</span>
</div>
</SidebarMenuButton>
</DropdownMenuTrigger>
+1 -1
View File
@@ -21,7 +21,7 @@ const { setOpenMobile } = useSidebar()
<SidebarMenuButton as-child :tooltip="item.title" :size="size" class="">
<NuxtLink
:to="item.link"
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"
class="group flex items-center gap-3 rounded-lg px-2 py-2 2xl:py-2.5 text-sm transition-all duration-200 md:!text-xs 2xl:!text-sm"
active-class="bg-primary text-white"
@click="setOpenMobile(false)"
>
@@ -1,15 +1,10 @@
<script setup lang="ts">
import { cn } from '~/lib/utils'
interface Item {
value: string
label: string
code?: string
priority?: number
}
import { type Item } from './index'
const props = defineProps<{
id: string
id?: string
modelValue?: string
items: Item[]
placeholder?: string
@@ -28,12 +23,17 @@ const open = ref(false)
const selectedItem = computed(() => props.items.find((item) => item.value === props.modelValue))
const displayText = computed(() => {
console.log(selectedItem);
if (selectedItem.value?.label) {
return selectedItem.value.label
}
return props.placeholder || 'Pilih item'
})
watch(props, () => {
console.log(props.modelValue);
})
const searchableItems = computed(() => {
const itemsWithSearch = props.items.map((item) => ({
...item,
@@ -74,10 +74,10 @@ function onSelect(item: Item) {
:aria-describedby="`${props.id}-search`"
:class="
cn(
'w-full justify-between border text-sm font-normal rounded-md px-3 py-2 focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
'w-full justify-between border dark:!border-slate-400 h-8 2xl:h-9 md:text-xs 2xl:text-sm font-normal rounded-md px-3 focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white',
{
'cursor-not-allowed bg-gray-100 opacity-50 border-gray-300 text-gray-500': props.isDisabled,
'bg-white text-black dark:bg-gray-800 dark:text-white dark:border-gray-600 border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700': !props.isDisabled,
'bg-white text-black dark:bg-slate-950 dark:text-white dark:border-gray-600 border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700': !props.isDisabled,
'text-gray-400 dark:text-gray-500': !modelValue && !props.isDisabled,
},
props.class,
@@ -117,7 +117,7 @@ function onSelect(item: Item) {
:value="item.searchValue"
:class="
cn(
'flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 text-sm',
'flex w-full cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 md:text-xs xl:text-sm',
'focus:outline-none text-black dark:text-white',
'hover:bg-primary hover:text-white focus:bg-primary focus:text-white',
'data-[highlighted]:bg-primary data-[highlighted]:text-white',
@@ -0,0 +1,23 @@
export interface Item {
value: string
label: string
code?: string
priority?: number
}
export function recStrToItem(input: Record<string, string>): Item[] {
const items: Item[] = []
let idx = 0;
for (const key in input) {
if (input.hasOwnProperty(key)) {
items.push({
value: key || ('unknown-' + idx),
label: input[key] || ('unknown-' + idx),
})
}
idx++
}
return items
}
export { default as Combobox } from './combobox.vue'
@@ -19,7 +19,7 @@ function changeTab(value: string) {
<template>
<!-- Tabs -->
<div class="mt-4 flex flex-wrap gap-2 rounded-md border bg-white p-4 shadow-sm">
<div class="mt-4 flex flex-wrap gap-2 rounded-md border bg-white dark:bg-neutral-950 p-4 shadow-sm">
<Button
v-for="tab in data"
:key="tab.value"
@@ -9,19 +9,7 @@ const props = defineProps<{
const recId = inject<Ref<number>>('rec_id')!
const recAction = inject<Ref<string>>('rec_action')!
const recItem = inject<Ref<any>>('rec_item')!
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
const activeKey = ref<string | null>(null)
const linkItems: LinkItem[] = [
{
label: 'Detail',
@@ -38,6 +26,18 @@ const linkItems: LinkItem[] = [
icon: 'i-lucide-pencil',
},
]
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
</script>
<template>
@@ -46,22 +46,29 @@ const linkItems: LinkItem[] = [
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white"
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" />
<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 bg-white" align="end">
<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"
v-slot="{ active }"
class="hover:bg-gray-100"
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="active ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
<Icon :name="item.icon ?? ''" />
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
@@ -9,25 +9,7 @@ const props = defineProps<{
const recId = inject<Ref<number>>('rec_id')!
const recAction = inject<Ref<string>>('rec_action')!
const recItem = inject<Ref<any>>('rec_item')!
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
const activeKey = ref<string | null>(null)
const linkItems: LinkItem[] = [
{
label: 'Detail',
@@ -58,6 +40,24 @@ const linkItems: LinkItem[] = [
icon: 'i-lucide-trash',
},
]
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
</script>
<template>
@@ -66,22 +66,29 @@ const linkItems: LinkItem[] = [
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white"
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" />
<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 bg-white" align="end">
<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"
v-slot="{ active }"
class="hover:bg-gray-100"
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="active ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
<Icon :name="item.icon ?? ''" />
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
@@ -9,25 +9,7 @@ const props = defineProps<{
const recId = inject<Ref<number>>('rec_id')!
const recAction = inject<Ref<string>>('rec_action')!
const recItem = inject<Ref<any>>('rec_item')!
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
const activeKey = ref<string | null>(null)
const linkItems: LinkItem[] = [
{
label: 'Detail',
@@ -51,6 +33,24 @@ const linkItems: LinkItem[] = [
icon: 'i-lucide-trash',
},
]
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
</script>
<template>
@@ -59,22 +59,29 @@ const linkItems: LinkItem[] = [
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white"
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" />
<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 bg-white" align="end">
<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"
v-slot="{ active }"
class="hover:bg-gray-100"
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="active ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
<Icon :name="item.icon ?? ''" />
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
@@ -9,31 +9,7 @@ const props = defineProps<{
const recId = inject<Ref<number>>('rec_id')!
const recAction = inject<Ref<string>>('rec_action')!
const recItem = inject<Ref<any>>('rec_item')!
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function process() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showProcess
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
const activeKey = ref<string | null>(null)
const linkItems: LinkItem[] = [
{
label: 'Proses',
@@ -64,6 +40,30 @@ const linkItems: LinkItem[] = [
icon: 'i-lucide-trash',
},
]
function detail() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showDetail
recItem.value = props.rec
}
function process() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showProcess
recItem.value = props.rec
}
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
</script>
<template>
@@ -72,22 +72,29 @@ const linkItems: LinkItem[] = [
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white"
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" />
<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 bg-white" align="end">
<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"
v-slot="{ active }"
class="hover:bg-gray-100"
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="active ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
<Icon :name="item.icon ?? ''" />
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
@@ -14,19 +14,7 @@ const props = withDefaults(defineProps<Props>(), {
const recId = inject<Ref<number>>('rec_id')!
const recAction = inject<Ref<string>>('rec_action')!
const recItem = inject<Ref<any>>('rec_item')!
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
const activeKey = ref<string | null>(null)
const linkItems: LinkItem[] = [
{
label: 'Edit',
@@ -43,6 +31,18 @@ const linkItems: LinkItem[] = [
icon: 'i-lucide-trash',
},
]
function edit() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showEdit
recItem.value = props.rec
}
function del() {
recId.value = props.rec.id || 0
recAction.value = ActionEvents.showConfirmDelete
recItem.value = props.rec
}
</script>
<template>
@@ -51,22 +51,29 @@ const linkItems: LinkItem[] = [
<DropdownMenuTrigger as-child>
<SidebarMenuButton
:size="size"
class="data-[state=open]:text-sidebar-accent-foreground data-[state=open]:bg-white"
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" />
<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 bg-white" align="end">
<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"
v-slot="{ active }"
class="hover:bg-gray-100"
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="active ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
<Icon :name="item.icon ?? ''" />
<span :class="activeKey === item.label ? 'text-sidebar-accent-foreground' : ''">{{ item.label }}</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
+3 -3
View File
@@ -60,11 +60,11 @@ const settingClass = computed(() => {
'[&_.label]:md:w-44 [&_.label]:xl:w-52',
][getLabelSizeIdx(props.labelSize)]
} else {
cls += ' [&_.label]:pb-1 ';
cls += ' [&_.label]:pb-1 [&_.label]:!pt-0 ';
}
cls += ' [&:not(.preview)_.height-default]:pt-2 [&:not(.preview)_.height-default]:2xl:!pt-1.5 [&:not(.preview)_.height-compact]:!pt-1 '
cls += '[&_textarea]:text-xs [&_textarea]:xl:text-sm '
cls += '[&_label]:text-xs [&_label]:xl:text-sm'
cls += '[&_textarea]:text-xs [&_textarea]:2xl:!text-sm '
cls += '[&_label]:text-xs [&_label]:2xl:!text-sm'
return cls
})
</script>
+1 -1
View File
@@ -9,6 +9,6 @@ const props = defineProps({
<template>
<div :class="`field ${props.defaultClass} ${props.class}`">
<slot />
<div v-if="props.errMessage" class="mt-1 text-xs font-medium text-red-500">{{ props.errMessage }}</div>
<div v-if="props.errMessage" class="mt-1 md:text-xs 2xl:text-sm font-medium text-red-500">{{ props.errMessage }}</div>
</div>
</template>
+1 -1
View File
@@ -32,7 +32,7 @@ const settingClass = computed(() => {
<template>
<div :class="settingClass">
<label>
<label v-bind="$attrs">
<slot />
</label>
</div>
@@ -20,7 +20,12 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
<template>
<ComboboxItem
v-bind="forwarded"
:class="cn('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm font-normal outline-none hover:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>*]:text-sm [&>*]:font-normal', props.class)"
:class="
cn(
'relative flex cursor-default select-none items-center rounded-sm bg-white px-2 py-1.5 text-sm font-normal text-black outline-none hover:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 [&>*]:text-sm [&>*]:font-normal',
props.class,
)
"
>
<slot />
</ComboboxItem>
@@ -20,11 +20,11 @@ function handleSelect(value: string) {
<div class="leaf-node min-w-max">
<CommandItem
:value="item.value"
class="flex items-center justify-between p-2 w-full text-sm font-normal hover:text-primary cursor-pointer rounded-md"
class="flex items-center justify-between p-2 w-full text-sm font-normal hover:text-primary cursor-pointer rounded-md bg-white dark:bg-transparent"
:class="{ 'pl-8': shouldAlign }"
@select="() => handleSelect(item.value)"
>
<span class="text-sm font-normal text-gray-400">{{ item.label }}</span>
<span class="text-sm font-normal text-gray-400 dark:text-gray-300">{{ item.label }}</span>
<Check
v-if="selectedValue === item.value"
class="w-4 h-4 text-primary ml-2 flex-shrink-0"
@@ -52,19 +52,27 @@ watch(isOpen, async (newValue) => {
<template>
<div class="tree-node min-w-max">
<Collapsible v-model:open="isOpen" class="w-full">
<Collapsible
v-model:open="isOpen"
class="w-full"
>
<!-- Node Header -->
<div class="flex items-center justify-start w-full p-2 rounded-md hover:bg-accent gap-2">
<div
class="flex w-full items-center justify-start gap-2 rounded-md bg-white p-2 hover:bg-accent dark:bg-transparent dark:hover:bg-slate-700"
>
<!-- Chevron Toggle Button -->
<CollapsibleTrigger as-child>
<Button
variant="ghost"
class="h-4 w-4 p-0 flex items-center justify-center"
class="flex h-4 w-4 items-center justify-center p-0"
>
<Loader2 v-if="isLoading" class="w-4 h-4 animate-spin text-muted-foreground" />
<Loader2
v-if="isLoading"
class="h-4 w-4 animate-spin text-muted-foreground"
/>
<ChevronRight
v-else
class="w-4 h-4 transition-transform duration-200 ease-in-out text-muted-foreground"
class="h-4 w-4 text-muted-foreground transition-transform duration-200 ease-in-out"
:class="{
'rotate-90': isChevronRotated,
}"
@@ -74,21 +82,24 @@ watch(isOpen, async (newValue) => {
<!-- Node Label -->
<span
class="text-sm font-normal cursor-pointer hover:text-primary flex-1 flex items-center justify-between"
class="flex flex-1 cursor-pointer items-center justify-between text-sm font-normal text-black hover:text-primary dark:text-white"
@click="handleLabelClick"
>
{{ item.label }}
<!-- Check Icon untuk selected state -->
<Check
v-if="selectedValue === item.value"
class="w-4 h-4 text-primary ml-2 flex-shrink-0"
class="ml-2 h-4 w-4 flex-shrink-0 text-primary"
/>
</span>
</div>
<!-- Children Container -->
<CollapsibleContent class="pl-6">
<div v-if="!hasChildren" class="text-sm text-muted-foreground p-2">
<div
v-if="!hasChildren"
class="p-2 text-sm text-muted-foreground"
>
{{ isLoading ? 'Memuat...' : 'Tidak ada data' }}
</div>
<TreeView
@@ -106,7 +117,7 @@ watch(isOpen, async (newValue) => {
<style scoped>
.tree-node {
@apply w-full;
width: 100%;
}
/* Animasi tambahan untuk smooth transition */
@@ -37,7 +37,7 @@ const filteredData = computed(() => {
// recursive filter
function filterTree(items: TreeItem[]): TreeItem[] {
return items
.map(item => {
.map((item) => {
const match = item.label.toLowerCase().includes(searchValue.value.toLowerCase())
let children: TreeItem[] | undefined = undefined
if (item.children) {
@@ -57,22 +57,28 @@ const filteredData = computed(() => {
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button variant="outline" role="combobox" class="w-full justify-between bg-white border-1 border-gray-400">
<Button
variant="outline"
role="combobox"
class="w-full justify-between border border-slate-200 bg-white text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
>
<span
class="font-normal text-muted-foreground" :class="cn(
'font-normal',
!modelValue && 'text-muted-foreground',
modelValue && 'text-black',
)"
class="font-normal text-muted-foreground"
:class="cn('font-normal', !modelValue && 'text-muted-foreground', modelValue && 'text-black')"
>
{{ selectedLabel }}
</span>
<ChevronsUpDown class="w-4 h-4 ml-2 opacity-50 shrink-0" />
<ChevronsUpDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="min-w-full max-w-[350px] p-0">
<PopoverContent
class="min-w-full max-w-[350px] border border-slate-200 bg-white p-0 text-black dark:border-slate-700 dark:bg-slate-800 dark:text-white"
>
<Command>
<CommandInput placeholder="Cari item..." v-model="searchValue" />
<CommandInput
placeholder="Cari item..."
v-model="searchValue"
/>
<CommandEmpty>Item tidak ditemukan.</CommandEmpty>
<CommandList class="max-h-[300px] overflow-x-auto overflow-y-auto">
<CommandGroup>
+2 -2
View File
@@ -4,7 +4,7 @@ import { cva } from 'class-variance-authority'
export { default as Button } from './Button.vue'
export const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md md:tex-xs xl:text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md md:text-xs 2xl:text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
@@ -19,7 +19,7 @@ export const buttonVariants = cva(
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'md:h8 xl:h-9 px-4 py-2',
default: 'md:h8 2xl:h-9 px-4 py-2',
xs: 'h-7 rounded px-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
+1 -1
View File
@@ -8,7 +8,7 @@ const props = defineProps<{
</script>
<template>
<div :class="cn('p-4 xl:p-5 2xl:p-6', props.class)">
<div :class="cn('p-4 2xl:p-5', props.class)">
<slot />
</div>
</template>
@@ -8,7 +8,7 @@ const props = defineProps<{
</script>
<template>
<p :class="cn('text-sm text-muted-foreground', props.class)">
<p :class="cn('text-xs 2xl:text-sm text-muted-foreground', props.class)">
<slot />
</p>
</template>
+1 -1
View File
@@ -8,7 +8,7 @@ const props = defineProps<{
</script>
<template>
<div :class="cn('flex items-center p-6 pt-0', props.class)">
<div :class="cn('flex items-center p-4 2xl:p-5 pt-0', props.class)">
<slot />
</div>
</template>
+1 -1
View File
@@ -21,7 +21,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
<CheckboxRoot
v-bind="forwarded"
:class="
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
cn('peer h-6 w-6 md:h-5 md:w-5 2xl:h-6 2xl:w-6 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
props.class)"
>
<CheckboxIndicator class="h-full w-full flex items-center justify-center text-current">
@@ -14,7 +14,7 @@ const { formDescriptionId } = useFormField()
<template>
<p
:id="formDescriptionId"
:class="cn('text-sm text-muted-foreground', props.class)"
:class="cn('md:text-xs 2xl:text-sm text-muted-foreground', props.class)"
>
<slot />
</p>
+1 -1
View File
@@ -24,7 +24,7 @@ const modelValue = useVModel(props, 'modelValue', emits, {
v-model="modelValue"
:class="
cn(
'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',
'border-input dark:bg-slate-950 ring-offset-background placeholder:text-muted-foreground flex h-8 2xl:h-9 w-full rounded-md border border-gray-400 px-3 py-2 md:text-xs 2xl: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,
)
"
+1 -1
View File
@@ -19,7 +19,7 @@ const delegatedProps = computed(() => {
v-bind="delegatedProps"
:class="
cn(
'md:!text-xs xl:text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
'md:!text-xs 2xl:text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class,
)
"
+5 -5
View File
@@ -30,7 +30,7 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
<template>
<div
v-if="collapsible === 'none'"
:class="cn('bg-sidebar text-sidebar-foreground flex h-full w-[--sidebar-width] flex-col', props.class)"
:class="cn('bg-sidebar text-sidebar-foreground flex h-full w-[--sidebar-width] 2xl:w-[--xxl-sidebar-width] flex-col', props.class)"
v-bind="$attrs"
>
<slot />
@@ -64,7 +64,7 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
<div
:class="
cn(
'relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear',
'relative h-svh w-[--sidebar-width] 2xl:w-[--xxl-sidebar-width] bg-transparent transition-[width] duration-200 ease-linear',
'group-data-[collapsible=offcanvas]:w-0',
'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset'
@@ -76,10 +76,10 @@ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
<div
:class="
cn(
'fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex',
'fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] 2xl:w-[--xxl-sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex',
side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] group-data-[collapsible=offcanvas]:2xl:left-[calc(var(--xxl-sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] group-data-[collapsible=offcanvas]:2xl:right-[calc(var(--xxl-sidebar-width)*-1)]',
// Adjust the padding for floating and inset variants.
variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_1rem_+_2px)]'

Some files were not shown because too many files have changed in this diff Show More