fix: add tree select subspecialist and filter doctor
This commit is contained in:
@@ -9,10 +9,12 @@ import { Input } from '~/components/pub/ui/input'
|
||||
import Select from '~/components/pub/ui/select/Select.vue'
|
||||
import Combobox from '~/components/pub/my-ui/combobox/combobox.vue'
|
||||
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
|
||||
import TreeSelect from '~/components/pub/my-ui/select-tree/tree-select.vue'
|
||||
|
||||
// Types
|
||||
import { IntegrationEncounterSchema, type IntegrationEncounterFormData } from '~/schemas/integration-encounter.schema'
|
||||
import type { PatientEntity } from '~/models/patient'
|
||||
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
||||
|
||||
// Helpers
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
@@ -23,6 +25,7 @@ const props = defineProps<{
|
||||
isReadonly?: boolean
|
||||
doctor?: any[]
|
||||
subSpecialist?: any[]
|
||||
specialists?: TreeItem[]
|
||||
payments: any[]
|
||||
participantGroups?: any[]
|
||||
seps: any[]
|
||||
@@ -60,18 +63,31 @@ const isReadonly = props.isReadonly !== undefined ? props.isReadonly : false
|
||||
const sepFileInput = ref<HTMLInputElement | null>(null)
|
||||
const sippFileInput = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const doctorOpts = ref([
|
||||
{ label: 'Pilih', value: '' },
|
||||
{ label: 'Dr. A', value: '1' },
|
||||
])
|
||||
|
||||
const subSpecialistOpts = ref([
|
||||
{ label: 'Pilih', value: '' },
|
||||
{ label: 'Subspesialis A', value: '1' },
|
||||
])
|
||||
const doctorOpts = computed(() => {
|
||||
// Add default option
|
||||
const defaultOption = [{ label: 'Pilih', value: '' }]
|
||||
// Add doctors from props
|
||||
const doctors = props.doctor || []
|
||||
return [...defaultOption, ...doctors]
|
||||
})
|
||||
|
||||
const isJKNPayment = computed(() => paymentType.value === 'jkn')
|
||||
|
||||
async function onFetchChildren(parentId: string): Promise<void> {
|
||||
console.log('onFetchChildren', parentId)
|
||||
}
|
||||
|
||||
// Watch specialist/subspecialist selection to fetch doctors
|
||||
watch(subSpecialistId, async (newValue) => {
|
||||
if (newValue) {
|
||||
console.log('SubSpecialist changed:', newValue)
|
||||
// Reset doctor selection
|
||||
doctorId.value = ''
|
||||
// Emit fetch event to parent
|
||||
emit('fetch', { subSpecialistId: newValue })
|
||||
}
|
||||
})
|
||||
|
||||
// Sync props to form fields
|
||||
watch(props, (value) => {
|
||||
const patient = value.patient || ({} as PatientEntity)
|
||||
@@ -233,17 +249,17 @@ const onSubmit = handleSubmit((values) => {
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Spesialis / Subspesialis</Label>
|
||||
<Label height="compact">
|
||||
Spesialis / Subspesialis
|
||||
<span class="text-red-500">*</span>
|
||||
</Label>
|
||||
<Field :errMessage="errors.subSpecialistId">
|
||||
<Combobox
|
||||
<TreeSelect
|
||||
id="subSpecialistId"
|
||||
v-model="subSpecialistId"
|
||||
v-bind="subSpecialistIdAttrs"
|
||||
:items="subSpecialistOpts"
|
||||
:is-disabled="isLoading || isReadonly"
|
||||
placeholder="Pilih Subspesialis"
|
||||
search-placeholder="Cari Subspesialis"
|
||||
empty-message="Subspesialis tidak ditemukan"
|
||||
:data="specialists || []"
|
||||
:on-fetch-children="onFetchChildren"
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
@@ -364,7 +380,14 @@ const onSubmit = handleSubmit((values) => {
|
||||
/>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">
|
||||
No. SEP
|
||||
@@ -392,14 +415,7 @@ const onSubmit = handleSubmit((values) => {
|
||||
</div>
|
||||
</Field>
|
||||
</Cell>
|
||||
</Block>
|
||||
|
||||
<Block
|
||||
labelSize="thin"
|
||||
class="!pt-0"
|
||||
:colCount="3"
|
||||
:cellFlex="false"
|
||||
>
|
||||
<Cell>
|
||||
<Label height="compact">Dokumen SEP</Label>
|
||||
<Field>
|
||||
|
||||
@@ -5,10 +5,18 @@ import AppViewPatient from '~/components/app/patient/view-patient.vue'
|
||||
|
||||
// Types
|
||||
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
|
||||
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'
|
||||
|
||||
// Constants
|
||||
import { paymentTypes, sepRefTypeCodes, participantGroups } from '~/lib/constants.vclaim'
|
||||
|
||||
// Services
|
||||
import {
|
||||
getList as getSpecialistList,
|
||||
getValueTreeItems as getSpecialistTreeItems,
|
||||
} from '~/services/specialist.service'
|
||||
import { getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
|
||||
|
||||
// Handlers
|
||||
import {
|
||||
patients,
|
||||
@@ -32,6 +40,8 @@ const isLoading = reactive<DataTableLoader>({
|
||||
const paymentsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const sepsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const participantGroupsList = ref<Array<{ value: string; label: string }>>([])
|
||||
const specialistsTree = ref<TreeItem[]>([])
|
||||
const doctorsList = ref<Array<{ value: string; label: string }>>([])
|
||||
|
||||
function handleSavePatient() {
|
||||
selectedPatientObject.value = null
|
||||
@@ -71,6 +81,74 @@ function handleEvent(menu: string, value?: any) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFetchSpecialists() {
|
||||
try {
|
||||
const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
|
||||
if (specialistsResult.success) {
|
||||
const specialists = specialistsResult.body?.data || []
|
||||
specialistsTree.value = getSpecialistTreeItems(specialists)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching specialist-subspecialist tree:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if a value exists in the specialistsTree
|
||||
* Returns true if it's a leaf node (subspecialist), false if parent node (specialist)
|
||||
*/
|
||||
function isSubspecialist(value: string, items: TreeItem[]): boolean {
|
||||
for (const item of items) {
|
||||
if (item.value === value) {
|
||||
// If this item has children, it's not selected, so skip
|
||||
// If this is the selected item, check if it has children in the tree
|
||||
return false // This means it's a specialist, not a subspecialist
|
||||
}
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
if (child.value === value) {
|
||||
// This is a subspecialist (leaf node)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async function handleFetchDoctors(subSpecialistId: string) {
|
||||
try {
|
||||
// Check if the selected value is a subspecialist or specialist
|
||||
const isSub = isSubspecialist(subSpecialistId, specialistsTree.value)
|
||||
|
||||
// Build filter based on selection type
|
||||
const filterParams: any = { 'page-size': 100 }
|
||||
|
||||
if (isSub) {
|
||||
// If selected is subspecialist, filter by subspecialist-id
|
||||
filterParams['subspecialist-id'] = subSpecialistId
|
||||
} else {
|
||||
// If selected is specialist, filter by specialist-id
|
||||
filterParams['specialist-id'] = subSpecialistId
|
||||
}
|
||||
|
||||
console.log('Fetching doctors with filter:', filterParams)
|
||||
|
||||
const doctors = await getDoctorValueLabelList(filterParams)
|
||||
doctorsList.value = doctors
|
||||
console.log('Fetched doctors:', doctors)
|
||||
} catch (error) {
|
||||
console.error('Error fetching doctors:', error)
|
||||
doctorsList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function handleFetch(value?: any) {
|
||||
if (value?.subSpecialistId) {
|
||||
handleFetchDoctors(value.subSpecialistId)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleInit() {
|
||||
paymentsList.value = Object.keys(paymentTypes).map((item) => ({
|
||||
value: item.toString(),
|
||||
@@ -84,6 +162,8 @@ async function handleInit() {
|
||||
value: item.toString(),
|
||||
label: participantGroups[item],
|
||||
})) as any
|
||||
// Fetch tree data
|
||||
await handleFetchSpecialists()
|
||||
}
|
||||
|
||||
provide('table_data_loader', isLoading)
|
||||
@@ -107,8 +187,11 @@ onMounted(async () => {
|
||||
:payments="paymentsList"
|
||||
:seps="sepsList"
|
||||
:participant-groups="participantGroupsList"
|
||||
:specialists="specialistsTree"
|
||||
:doctor="doctorsList"
|
||||
:patient="selectedPatientObject"
|
||||
@event="handleEvent"
|
||||
@fetch="handleFetch"
|
||||
/>
|
||||
|
||||
<AppViewPatient
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { type Base, genBase } from "./_base"
|
||||
import type { Subspecialist } from "./subspecialist"
|
||||
|
||||
export interface Specialist extends Base {
|
||||
code: string
|
||||
name: string
|
||||
unit_id?: number | string | null
|
||||
subspecialists?: Subspecialist[]
|
||||
}
|
||||
|
||||
export function genSpecialist(): Specialist {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Base
|
||||
import * as base from './_crud-base'
|
||||
|
||||
// Types
|
||||
import type { Doctor } from '~/models/doctor'
|
||||
|
||||
const path = '/api/v1/doctor'
|
||||
const name = 'doctor'
|
||||
|
||||
export function create(data: any) {
|
||||
return base.create(path, data, name)
|
||||
}
|
||||
|
||||
export function getList(params: any = null) {
|
||||
return base.getList(path, params, name)
|
||||
}
|
||||
|
||||
export function getDetail(id: number | string) {
|
||||
return base.getDetail(path, id, name)
|
||||
}
|
||||
|
||||
export function update(id: number | string, data: any) {
|
||||
return base.update(path, id, data, name)
|
||||
}
|
||||
|
||||
export function remove(id: number | string) {
|
||||
return base.remove(path, id, name)
|
||||
}
|
||||
|
||||
export async function getValueLabelList(params: any = null): Promise<{ value: string; label: string }[]> {
|
||||
let data: { value: string; label: string }[] = []
|
||||
const result = await getList(params)
|
||||
if (result.success) {
|
||||
const resultData = result.body?.data || []
|
||||
data = resultData.map((item: Doctor) => ({
|
||||
value: item.id ? Number(item.id) : item.id,
|
||||
label: item.employee?.person?.name || '',
|
||||
}))
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import * as base from './_crud-base'
|
||||
|
||||
// Types
|
||||
import type { Specialist } from '~/models/specialist'
|
||||
import type { TreeItem } from '~/models/_base'
|
||||
|
||||
const path = '/api/v1/specialist'
|
||||
const name = 'specialist'
|
||||
@@ -40,3 +41,20 @@ export async function getValueLabelList(params: any = null): Promise<{ value: st
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert specialist response to TreeItem[] with subspecialist children
|
||||
* @param specialists Array of specialist objects from API
|
||||
* @returns TreeItem[]
|
||||
*/
|
||||
export function getValueTreeItems(specialists: any[]): TreeItem[] {
|
||||
return specialists.map((specialist: Specialist) => ({
|
||||
value: specialist.id ? String(specialist.id) : specialist.code,
|
||||
label: specialist.name,
|
||||
hasChildren: Array.isArray(specialist.subspecialists) && specialist.subspecialists.length > 0,
|
||||
children:
|
||||
Array.isArray(specialist.subspecialists) && specialist.subspecialists.length > 0
|
||||
? getValueTreeItems(specialist.subspecialists)
|
||||
: undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user