fix: add tree select subspecialist and filter doctor

This commit is contained in:
riefive
2025-10-28 10:02:12 +07:00
parent 803139cc17
commit 99c5266f2b
5 changed files with 183 additions and 23 deletions
+39 -23
View File
@@ -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
+2
View File
@@ -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 {
+41
View File
@@ -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
}
+18
View File
@@ -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,
}))
}