Files
web-antrean/stores/ruangStore.js
T
2026-02-03 15:11:55 +07:00

540 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// stores/ruangStore.js
// Merged dari masterStore.ruangData + klinikruangstore.klinikRuangList
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { useClinicStore } from './clinicStore';
export const useRuangStore = defineStore('ruang', () => {
const clinicStore = useClinicStore();
// State/Computed - List Master Klinik (disinkronkan dengan clinicStore)
const masterKlinikList = computed(() => {
const baseList = typeof clinicStore.getClinicsForDropdown === 'function'
? clinicStore.getClinicsForDropdown()
: [];
return baseList.map((c) => ({
kode: c.kode,
nama: c.name,
id: c.id,
}));
});
// State - Ruang Data (merged dari masterStore.ruangData + klinikruangstore.klinikRuangList)
const ruangData = ref([
{
id: 1,
no: 1,
kodeKlinik: 'AN',
namaKlinik: 'ANAK',
namaRuang: 'R. TINDAKAN',
ruangList: [
{ nomorRuang: '1', namaRuang: 'R. TINDAKAN', nomorScreen: '101' }
]
},
{
id: 2,
no: 2,
kodeKlinik: 'AS',
namaKlinik: 'ANESTESI',
namaRuang: 'Ruang 1, Ruang 2, Ruang 3, Ruang 4, Ruang 5, Ruang 6',
ruangList: [
{ nomorRuang: '1', namaRuang: 'Ruang 1', nomorScreen: '201' },
{ nomorRuang: '2', namaRuang: 'Ruang 2', nomorScreen: '202' },
{ nomorRuang: '3', namaRuang: 'Ruang 3', nomorScreen: '203' },
{ nomorRuang: '4', namaRuang: 'Ruang 4', nomorScreen: '204' },
{ nomorRuang: '5', namaRuang: 'Ruang 5', nomorScreen: '205' },
{ nomorRuang: '6', namaRuang: 'Ruang 6', nomorScreen: '206' }
]
},
{
id: 3,
no: 3,
kodeKlinik: 'BD',
namaKlinik: 'BEDAH',
namaRuang: 'Ruang Konsultasi',
ruangList: [
{ nomorRuang: '1', namaRuang: 'Ruang Konsultasi', nomorScreen: '301' }
]
},
{
id: 4,
no: 4,
kodeKlinik: 'GR',
namaKlinik: 'GERIATRI',
namaRuang: 'Ruang Pemeriksaan',
ruangList: [
{ nomorRuang: '1', namaRuang: 'Ruang Pemeriksaan', nomorScreen: '401' }
]
},
{
id: 5,
no: 5,
kodeKlinik: 'GI',
namaKlinik: 'GIGI DAN MULUT',
namaRuang: 'Ruang 1, Ruang 2, Ruang 3',
ruangList: [
{ nomorRuang: '1', namaRuang: 'Ruang 1', nomorScreen: '501' },
{ nomorRuang: '2', namaRuang: 'Ruang 2', nomorScreen: '502' },
{ nomorRuang: '3', namaRuang: 'Ruang 3', nomorScreen: '503' }
]
},
]);
// Computed
const totalKlinikRuang = computed(() => ruangData.value.length);
const totalRuangan = computed(() => {
return ruangData.value.reduce((total, klinik) => {
return total + klinik.ruangList.length;
}, 0);
});
// Get all ruang list (for antrian display)
const getAllRuangList = computed(() => {
const allRuang = [];
ruangData.value.forEach(klinik => {
klinik.ruangList.forEach(ruang => {
allRuang.push({
kodeKlinik: klinik.kodeKlinik,
namaKlinik: klinik.namaKlinik,
...ruang
});
});
});
return allRuang;
});
// Getters
const getKlinikByCode = (kode) => {
return ruangData.value.find(k => k.kodeKlinik === kode);
};
const getRuangByKlinik = (kodeKlinik) => {
return ruangData.value.filter(r => r.kodeKlinik === kodeKlinik);
};
// Actions - CRUD Operations
const createKlinikRuang = (data) => {
const newId = Math.max(...ruangData.value.map(r => r.id), 0) + 1;
const newNo = ruangData.value.length + 1;
// Generate nama ruang untuk display
const namaRuangDisplay = data.ruangList
.map(r => r.namaRuang)
.filter(n => n)
.join(', ');
const newKlinikRuang = {
id: newId,
no: newNo,
kodeKlinik: data.kodeKlinik,
namaKlinik: data.namaKlinik,
namaRuang: namaRuangDisplay,
ruangList: data.ruangList
};
ruangData.value.push(newKlinikRuang);
return {
success: true,
message: `Klinik Ruang ${data.namaKlinik} berhasil ditambahkan`,
data: newKlinikRuang
};
};
const updateKlinikRuang = (id, data) => {
const index = ruangData.value.findIndex(r => r.id === id);
if (index === -1) {
return {
success: false,
message: 'Klinik Ruang tidak ditemukan'
};
}
// Generate nama ruang untuk display
const namaRuangDisplay = data.ruangList
.map(r => r.namaRuang)
.filter(n => n)
.join(', ');
ruangData.value[index] = {
...ruangData.value[index],
kodeKlinik: data.kodeKlinik,
namaKlinik: data.namaKlinik,
namaRuang: namaRuangDisplay,
ruangList: data.ruangList
};
return {
success: true,
message: `Klinik Ruang ${data.namaKlinik} berhasil diupdate`,
data: ruangData.value[index]
};
};
const deleteKlinikRuang = (id) => {
const index = ruangData.value.findIndex(r => r.id === id);
if (index === -1) {
return {
success: false,
message: 'Klinik Ruang tidak ditemukan'
};
}
const deletedKlinik = ruangData.value[index];
ruangData.value.splice(index, 1);
// Reorder numbers
ruangData.value.forEach((r, idx) => {
r.no = idx + 1;
});
return {
success: true,
message: `Klinik Ruang ${deletedKlinik.namaKlinik} berhasil dihapus`
};
};
const addRuangToKlinik = (klinikId, ruangDataItem) => {
const klinik = ruangData.value.find(k => k.id === klinikId);
if (!klinik) {
return {
success: false,
message: 'Klinik tidak ditemukan'
};
}
klinik.ruangList.push(ruangDataItem);
// Update display name
klinik.namaRuang = klinik.ruangList
.map(r => r.namaRuang)
.filter(n => n)
.join(', ');
return {
success: true,
message: `Ruang ${ruangDataItem.namaRuang} berhasil ditambahkan`,
data: klinik
};
};
const removeRuangFromKlinik = (klinikId, ruangIndex) => {
const klinik = ruangData.value.find(k => k.id === klinikId);
if (!klinik) {
return {
success: false,
message: 'Klinik tidak ditemukan'
};
}
if (klinik.ruangList.length <= 1) {
return {
success: false,
message: 'Minimal harus ada 1 ruangan'
};
}
const removedRuang = klinik.ruangList[ruangIndex];
klinik.ruangList.splice(ruangIndex, 1);
// Update display name
klinik.namaRuang = klinik.ruangList
.map(r => r.namaRuang)
.filter(n => n)
.join(', ');
return {
success: true,
message: `Ruang ${removedRuang.namaRuang} berhasil dihapus`,
data: klinik
};
};
const searchKlinikRuang = (searchTerm) => {
if (!searchTerm) return ruangData.value;
const term = searchTerm.toLowerCase();
return ruangData.value.filter(k =>
k.kodeKlinik.toLowerCase().includes(term) ||
k.namaKlinik.toLowerCase().includes(term) ||
k.namaRuang.toLowerCase().includes(term)
);
};
// Alias methods untuk backward compatibility dengan masterStore
const addRuang = (ruangPayload) => {
return createKlinikRuang(ruangPayload);
};
const updateRuang = (ruangPayload) => {
return updateKlinikRuang(ruangPayload.id, ruangPayload);
};
const deleteRuang = (ruangId) => {
return deleteKlinikRuang(ruangId);
};
// New method for replacing all rooms (used by MasterKlinikRuang when generating from API)
const replaceAllRooms = (newRoomsData) => {
// Group rooms by clinic CODE + JENIS LAYANAN (to keep Reguler and Eksekutif separate)
const groupedByClinic = {};
newRoomsData.forEach(room => {
// Use kodeKlinik + jenisLayanan as key to separate same clinic with different service types
const key = `${room.kodeKlinik}-${room.jenisLayanan}`;
if (!groupedByClinic[key]) {
groupedByClinic[key] = {
id: room.id || generateRoomId(),
kodeKlinik: room.kodeKlinik,
namaKlinik: room.namaKlinik,
jenisLayanan: room.jenisLayanan,
ruangList: []
};
}
groupedByClinic[key].ruangList.push({
nomorRuang: room.nomorRuang,
namaRuang: room.namaRuang,
nomorScreen: room.nomorScreen,
kodeRuang: room.kodeRuang
});
});
// Convert to array and add display names
const newRuangData = Object.values(groupedByClinic).map((clinic, index) => ({
...clinic,
no: index + 1,
namaRuang: clinic.ruangList.map(r => r.namaRuang).join(', ')
}));
ruangData.value = newRuangData;
return {
success: true,
message: `${newRuangData.length} klinik dengan total ${newRoomsData.length} ruangan berhasil dimuat`
};
};
/**
* Fetch room data from API and merge with clinic metadata
* preserving seed/local data (Eksekutif)
*/
const fetchRuangFromAPI = async (force = false) => {
try {
console.log('🔄 Fetching rooms from API...');
// 1. Ensure clinics are loaded (from clinicStore)
// clinicStore.fetchRegulerClinics is already called in MasterKlinikRuang.vue
// but we use the existing clinics in the store
const allClinics = clinicStore.clinics;
// 2. Fetch room data from new API
const response = await fetch('http://10.10.150.131:8089/api/v1/loket/ruang');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const rawData = await response.json();
const apiRoomsRaw = rawData.data || [];
console.log('📥 Received API rooms:', apiRoomsRaw.length);
// 3. Prepare rooms list (API + Seed)
const mergedRooms = [];
let roomIdCounter = 1;
// Track which clinics are handled by API to avoid duplicates for Reguler
const clinicCodesInAPI = new Set();
// Process API Clinics and their nested Rooms
apiRoomsRaw.forEach(apiClinic => {
const clinicId = apiClinic.idklinik;
const clinicCode = apiClinic.kodeloket || apiClinic.kodeklinik;
const apiRuangan = apiClinic.ruangan || [];
// Find clinic metadata in store
// Find clinic metadata in store - Specifically look for Reguler clinic first
// as API data (idklinik, kodeloket) is for Reguler clinics.
const clinic = allClinics.find(c =>
c.jenisLayanan === 'Reguler' && (
(clinicId && String(c.id) === String(clinicId)) ||
(clinicCode && String(c.kode) === String(clinicCode)) ||
(apiClinic.namaklinik && c.name.toUpperCase() === apiClinic.namaklinik.toUpperCase())
)
) || allClinics.find(c =>
(clinicId && String(c.id) === String(clinicId)) ||
(clinicCode && String(c.kode) === String(clinicCode)) ||
(apiClinic.namaklinik && c.name.toUpperCase() === apiClinic.namaklinik.toUpperCase())
);
if (clinic) {
clinicCodesInAPI.add(clinic.kode);
// Check if API room data is "generic" (only 1 room with same name as clinic)
// to decide whether to prefer Specialist fallback
const isGenericSingleRoom = apiRuangan.length === 1 &&
(apiRuangan[0].namaruangan?.trim().toUpperCase() === clinic.name.toUpperCase() ||
!apiRuangan[0].namaruangan ||
apiRuangan[0].namaruangan === '-');
const hasManySpecialists = clinic.spesialis && clinic.spesialis.length > 1;
// Priority 1: Use nested "ruangan" array from new API
// Skip this if it's just 1 generic room and we have better data in Specialists
if (apiRuangan.length > 0 && !(isGenericSingleRoom && hasManySpecialists)) {
apiRuangan.forEach((apiRoom, index) => {
const specialistName = (clinic.spesialis && clinic.spesialis[index])
? clinic.spesialis[index].Spesialis
: null;
const fallbackName = `Ruang ${apiRoom.idruangan || apiRoom.nomor_ruang || index + 1}`;
mergedRooms.push({
id: roomIdCounter++,
kodeKlinik: clinic.kode,
namaKlinik: clinic.name,
kodeRuang: apiRoom.idruangan || apiRoom.idruang || `${clinic.kode}-${roomIdCounter}`,
namaRuang: apiRoom.namaruangan || apiRoom.nama_ruang || apiRoom.nama || specialistName || fallbackName,
nomorRuang: apiRoom.idruangan || apiRoom.nomor_ruang || (index + 1).toString(),
nomorScreen: apiRoom.nomor_screen || `${clinic.kode}${apiRoom.idruangan || index + 1}`,
jenisLayanan: clinic.jenisLayanan || 'Reguler'
});
});
}
// Priority 2: Fallback to specialist metadata (Old API) if new API is empty OR generic
else if (clinic.spesialis && clinic.spesialis.length > 0) {
console.log(`️ Using specialists fallback for ${clinic.name} (${apiRuangan.length === 1 ? 'API generic' : 'API empty'})`);
clinic.spesialis.forEach((spec, index) => {
mergedRooms.push({
id: roomIdCounter++,
kodeKlinik: clinic.kode,
namaKlinik: clinic.name,
kodeRuang: `${clinic.kode}-${spec.idspesialis}`,
namaRuang: spec.Spesialis,
nomorRuang: (index + 1).toString(),
nomorScreen: `${clinic.kode}${index + 1}`,
jenisLayanan: 'Reguler'
});
});
}
// Priority 3: Single room fallback
else {
const fallbackName = apiClinic.nama_ruang || apiClinic.nama || clinic.name || 'Ruang 1';
mergedRooms.push({
id: roomIdCounter++,
kodeKlinik: clinic.kode,
namaKlinik: clinic.name,
kodeRuang: apiClinic.idruang || clinic.kode,
namaRuang: fallbackName,
nomorRuang: apiClinic.nomor_ruang || '1',
nomorScreen: apiClinic.nomor_screen || `${clinic.kode}1`,
jenisLayanan: clinic.jenisLayanan || 'Reguler'
});
}
}
});
// 4. Add Seed Data (Eksekutif) + Reguler clinics not in API
allClinics.forEach(clinic => {
if (clinic.jenisLayanan === 'Eksekutif' || !clinicCodesInAPI.has(clinic.kode)) {
console.log(` Adding clinic from seed/metadata: ${clinic.name} (${clinic.jenisLayanan})`);
// Fallback logic similar to original generateRoomsFromClinics
if (clinic.jenisLayanan === 'Reguler' && clinic.spesialis?.length > 0) {
clinic.spesialis.forEach((spec, index) => {
mergedRooms.push({
id: roomIdCounter++,
kodeKlinik: clinic.kode,
namaKlinik: clinic.name,
kodeRuang: `${clinic.kode}-${spec.idspesialis}`,
namaRuang: spec.Spesialis,
nomorRuang: (index + 1).toString(),
nomorScreen: `${clinic.kode}${index + 1}`,
jenisLayanan: 'Reguler'
});
});
} else {
mergedRooms.push({
id: roomIdCounter++,
kodeKlinik: clinic.kode,
namaKlinik: clinic.name,
kodeRuang: clinic.kode,
namaRuang: clinic.name,
nomorRuang: '1',
nomorScreen: `${clinic.kode}1`,
jenisLayanan: clinic.jenisLayanan || 'Reguler'
});
}
}
});
// 5. Update store
const result = replaceAllRooms(mergedRooms);
return {
success: true,
message: `Berhasil sinkronisasi ${apiRoomsRaw.length} ruang dari API dan ${allClinics.length} data klinik`
};
} catch (error) {
console.error('❌ Error in fetchRuangFromAPI:', error);
return {
success: false,
message: `Gagal memuat data ruang: ${error.message}`
};
}
};
// Helper to generate unique room ID
const generateRoomId = () => {
return Date.now() + Math.floor(Math.random() * 1000);
};
return {
// State
masterKlinikList,
ruangData,
// Computed
totalKlinikRuang,
totalRuangan,
getAllRuangList,
// Getters
getKlinikByCode,
getRuangByKlinik,
// Actions (new names)
createKlinikRuang,
updateKlinikRuang,
deleteKlinikRuang,
addRuangToKlinik,
removeRuangFromKlinik,
searchKlinikRuang,
// Alias for backward compatibility
addRuang,
updateRuang,
deleteRuang,
// New method for API-based room generation
replaceAllRooms,
fetchRuangFromAPI,
};
}, {
persist: {
key: 'ruang-store-state',
storage: typeof window !== 'undefined' ? localStorage : undefined,
paths: ['ruangData'],
},
});