-
+
+
-
+
diff --git a/app/pages/(features)/rehab/encounter/add.vue b/app/pages/(features)/rehab/encounter/add.vue
index f20b90c1..fe7dd98c 100644
--- a/app/pages/(features)/rehab/encounter/add.vue
+++ b/app/pages/(features)/rehab/encounter/add.vue
@@ -35,7 +35,15 @@ const canCreate = hasCreateAccess(roleAccess)
-
+
-
+
diff --git a/app/pages/(features)/rehab/encounter/index.vue b/app/pages/(features)/rehab/encounter/index.vue
index 7a8564a8..6bbec942 100644
--- a/app/pages/(features)/rehab/encounter/index.vue
+++ b/app/pages/(features)/rehab/encounter/index.vue
@@ -33,8 +33,15 @@ const canRead = hasReadAccess(roleAccess)
diff --git a/app/schemas/integration-bpjs.schema.ts b/app/schemas/integration-bpjs.schema.ts
new file mode 100644
index 00000000..47206edf
--- /dev/null
+++ b/app/schemas/integration-bpjs.schema.ts
@@ -0,0 +1,255 @@
+import { z } from 'zod'
+
+const ERROR_MESSAGES = {
+ required: {
+ sepDate: 'Tanggal wajib diisi',
+ serviceType: 'Jenis Pelayanan wajib diisi',
+ admissionType: 'Jenis Pendaftaran wajib diisi',
+ cardNumber: 'No. Kartu BPJS wajib diisi',
+ nationalId: 'Nomor ID wajib diisi',
+ medicalRecordNumber: 'Nomor Rekam Medis wajib diisi',
+ patientName: 'Nama wajib diisi',
+ phoneNumber: 'Nomor Telepon wajib diisi',
+ referralLetterNumber: 'Nomor Surat Kontrol wajib diisi',
+ referralLetterDate: 'Tanggal Surat Kontrol wajib diisi',
+ fromClinic: 'Faskes Asal wajib diisi',
+ destinationClinic: 'Klinik Tujuan wajib diisi',
+ attendingDoctor: 'Dokter wajib diisi',
+ initialDiagnosis: 'Diagnosa Awal wajib diisi',
+ cob: 'COB wajib diisi',
+ cataract: 'Katarak wajib diisi',
+ clinicExcecutive: 'Klinkik eksekutif wajib diisi',
+ subSpecialistId: 'Subspesialis wajib diisi',
+ procedureType: 'Jenis Prosedur wajib diisi',
+ supportCode: 'Kode Penunjang wajib diisi',
+ note: 'Catatan wajib diisi',
+ trafficAccident: 'Kejadian lalu lintas wajib diisi',
+ purposeOfVisit: 'Tujuan Kunjungan wajib diisi',
+ serviceAssessment: 'Assemen Pelayanan wajib diisi',
+ lpNumber: 'Nomor LP wajib diisi',
+ accidentDate: 'Tanggal Kejadian lalu lintas wajib diisi',
+ accidentNote: 'Keterangan Kejadian lalu lintas wajib diisi',
+ accidentProvince: 'Provinsi Kejadian lalu lintas wajib diisi',
+ accidentCity: 'Kota Kejadian lalu lintas wajib diisi',
+ accidentDistrict: 'Kecamatan Kejadian lalu lintas wajib diisi',
+ suplesi: 'Suplesi wajib diisi',
+ suplesiNumber: 'Nomor Suplesi wajib diisi',
+ classLevel: 'Kelas Rawat wajib diisi',
+ classLevelUpgrade: 'Kelas Rawat Naik wajib diisi',
+ classPaySource: 'Pembiayaan wajib diisi',
+ responsiblePerson: 'Penanggung Jawab wajib diisi',
+ },
+}
+
+const IntegrationBpjsSchema = z
+ .object({
+ sepDate: z.string({ required_error: ERROR_MESSAGES.required.sepDate }).min(1, ERROR_MESSAGES.required.sepDate),
+ serviceType: z
+ .string({ required_error: ERROR_MESSAGES.required.serviceType })
+ .min(1, ERROR_MESSAGES.required.serviceType)
+ .optional(),
+ admissionType: z
+ .string({ required_error: ERROR_MESSAGES.required.admissionType })
+ .min(1, ERROR_MESSAGES.required.admissionType),
+ cardNumber: z
+ .string({ required_error: ERROR_MESSAGES.required.cardNumber })
+ .min(1, ERROR_MESSAGES.required.cardNumber),
+ nationalId: z
+ .string({ required_error: ERROR_MESSAGES.required.nationalId })
+ .min(1, ERROR_MESSAGES.required.nationalId),
+ medicalRecordNumber: z
+ .string({ required_error: ERROR_MESSAGES.required.medicalRecordNumber })
+ .min(1, ERROR_MESSAGES.required.medicalRecordNumber),
+ patientName: z
+ .string({ required_error: ERROR_MESSAGES.required.patientName })
+ .min(1, ERROR_MESSAGES.required.patientName),
+ phoneNumber: z
+ .string({ required_error: ERROR_MESSAGES.required.phoneNumber })
+ .min(1, ERROR_MESSAGES.required.phoneNumber),
+ referralLetterNumber: z
+ .string({ required_error: ERROR_MESSAGES.required.referralLetterNumber })
+ .min(1, ERROR_MESSAGES.required.referralLetterNumber).optional(),
+ referralLetterDate: z
+ .string({ required_error: ERROR_MESSAGES.required.referralLetterDate })
+ .min(1, ERROR_MESSAGES.required.referralLetterDate).optional(),
+ fromClinic: z
+ .string({ required_error: ERROR_MESSAGES.required.fromClinic })
+ .min(1, ERROR_MESSAGES.required.fromClinic)
+ .optional(),
+ destinationClinic: z
+ .string({ required_error: ERROR_MESSAGES.required.destinationClinic })
+ .min(1, ERROR_MESSAGES.required.destinationClinic),
+ attendingDoctor: z
+ .string({ required_error: ERROR_MESSAGES.required.attendingDoctor })
+ .min(1, ERROR_MESSAGES.required.attendingDoctor),
+ initialDiagnosis: z
+ .string({ required_error: ERROR_MESSAGES.required.initialDiagnosis })
+ .min(1, ERROR_MESSAGES.required.initialDiagnosis),
+ cob: z.string({ required_error: ERROR_MESSAGES.required.cob }).min(1, ERROR_MESSAGES.required.cob),
+ cataract: z.string({ required_error: ERROR_MESSAGES.required.cataract }).min(1, ERROR_MESSAGES.required.cataract),
+ clinicExcecutive: z
+ .string({ required_error: ERROR_MESSAGES.required.clinicExcecutive })
+ .min(1, ERROR_MESSAGES.required.clinicExcecutive),
+ subSpecialistId: z
+ .string({ required_error: ERROR_MESSAGES.required.subSpecialistId })
+ .min(1, ERROR_MESSAGES.required.subSpecialistId)
+ .optional(),
+ procedureType: z
+ .string({ required_error: ERROR_MESSAGES.required.procedureType })
+ .min(1, ERROR_MESSAGES.required.procedureType)
+ .optional(),
+ supportCode: z
+ .string({ required_error: ERROR_MESSAGES.required.supportCode })
+ .min(1, ERROR_MESSAGES.required.supportCode)
+ .optional(),
+ note: z.string({ required_error: ERROR_MESSAGES.required.note }).min(1, ERROR_MESSAGES.required.note).optional(),
+ trafficAccident: z
+ .string({ required_error: ERROR_MESSAGES.required.trafficAccident })
+ .min(1, ERROR_MESSAGES.required.trafficAccident)
+ .optional(),
+ purposeOfVisit: z
+ .string({ required_error: ERROR_MESSAGES.required.purposeOfVisit })
+ .min(1, ERROR_MESSAGES.required.purposeOfVisit)
+ .optional(),
+ serviceAssessment: z
+ .string({ required_error: ERROR_MESSAGES.required.serviceAssessment })
+ .min(1, ERROR_MESSAGES.required.serviceAssessment)
+ .optional(),
+ lpNumber: z.string({ required_error: ERROR_MESSAGES.required.lpNumber }).optional(),
+ accidentDate: z.string({ required_error: ERROR_MESSAGES.required.accidentDate }).optional(),
+ accidentNote: z.string({ required_error: ERROR_MESSAGES.required.accidentNote }).optional(),
+ accidentProvince: z.string({ required_error: ERROR_MESSAGES.required.accidentProvince }).optional(),
+ accidentCity: z.string({ required_error: ERROR_MESSAGES.required.accidentCity }).optional(),
+ accidentDistrict: z.string({ required_error: ERROR_MESSAGES.required.accidentDistrict }).optional(),
+ suplesi: z.string({ required_error: ERROR_MESSAGES.required.suplesi }).optional(),
+ suplesiNumber: z.string({ required_error: ERROR_MESSAGES.required.suplesiNumber }).optional(),
+ classLevel: z.string({ required_error: ERROR_MESSAGES.required.classLevel }).optional(),
+ classLevelUpgrade: z.string({ required_error: ERROR_MESSAGES.required.classLevelUpgrade }).optional(),
+ classPaySource: z.string({ required_error: ERROR_MESSAGES.required.classPaySource }).optional(),
+ responsiblePerson: z.string({ required_error: ERROR_MESSAGES.required.responsiblePerson }).optional(),
+ })
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '') {
+ return data.accidentDate && data.accidentDate.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.accidentDate,
+ path: ['accidentDate'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '') {
+ return data.accidentProvince && data.accidentProvince.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.accidentProvince,
+ path: ['accidentProvince'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '') {
+ return data.accidentCity && data.accidentCity.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.accidentCity,
+ path: ['accidentCity'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '') {
+ return data.accidentDistrict && data.accidentDistrict.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.accidentDistrict,
+ path: ['accidentDistrict'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '') {
+ return data.suplesi && data.suplesi.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.suplesi,
+ path: ['suplesi'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.trafficAccident && data.trafficAccident.trim() !== '' && data.suplesi?.trim() === 'yes') {
+ return data.suplesiNumber && data.suplesiNumber.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.suplesiNumber,
+ path: ['suplesiNumber'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.serviceType === '1') {
+ return data.classLevel && data.classLevel.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.classLevel,
+ path: ['classLevel'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.serviceType === '1') {
+ return data.classLevelUpgrade && data.classLevelUpgrade.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.classLevelUpgrade,
+ path: ['classLevelUpgrade'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.serviceType === '1' && data.classLevelUpgrade?.trim() !== '') {
+ return data.classPaySource && data.classPaySource.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.classPaySource,
+ path: ['classPaySource'],
+ },
+ )
+ .refine(
+ (data) => {
+ if (data.serviceType === '1' && data.classPaySource?.trim() !== '') {
+ return data.responsiblePerson && data.responsiblePerson.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.responsiblePerson,
+ path: ['responsiblePerson'],
+ },
+ )
+
+type IntegrationBpjsFormData = z.infer
+
+export { IntegrationBpjsSchema }
+export type { IntegrationBpjsFormData }
diff --git a/app/schemas/integration-encounter.schema.ts b/app/schemas/integration-encounter.schema.ts
new file mode 100644
index 00000000..32327328
--- /dev/null
+++ b/app/schemas/integration-encounter.schema.ts
@@ -0,0 +1,133 @@
+import { z } from 'zod'
+
+const ERROR_MESSAGES = {
+ required: {
+ doctorId: 'Dokter wajib diisi',
+ registerDate: 'Tanggal Daftar wajib diisi',
+ paymentType: 'Jenis Pembayaran wajib diisi',
+ subSpecialistId: 'Subspesialis wajib diisi',
+ patientCategory: 'Kelompok Peserta wajib diisi',
+ cardNumber: 'No. Kartu BPJS wajib diisi',
+ sepType: 'Jenis SEP wajib diisi',
+ sepNumber: 'No. SEP wajib diisi',
+ },
+}
+
+const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
+
+const IntegrationEncounterSchema = z
+ .object({
+ // Patient data (readonly, populated from selected patient)
+ patientName: z.string().optional(),
+ nationalIdentity: z.string().optional(),
+ medicalRecordNumber: z.string().optional(),
+
+ // Visit data
+ doctorId: z
+ .string({ required_error: ERROR_MESSAGES.required.doctorId })
+ .min(1, ERROR_MESSAGES.required.doctorId),
+ subSpecialistId: z
+ .string({ required_error: ERROR_MESSAGES.required.subSpecialistId })
+ .min(1, ERROR_MESSAGES.required.subSpecialistId)
+ .optional(),
+ registerDate: z
+ .string({ required_error: ERROR_MESSAGES.required.registerDate })
+ .min(1, ERROR_MESSAGES.required.registerDate),
+ paymentType: z
+ .string({ required_error: ERROR_MESSAGES.required.paymentType })
+ .min(1, ERROR_MESSAGES.required.paymentType),
+
+ // BPJS related fields
+ patientCategory: z
+ .string()
+ .min(1, ERROR_MESSAGES.required.patientCategory)
+ .optional(),
+ cardNumber: z
+ .string()
+ .min(1, ERROR_MESSAGES.required.cardNumber)
+ .optional(),
+ sepType: z
+ .string()
+ .min(1, ERROR_MESSAGES.required.sepType)
+ .optional(),
+ sepNumber: z
+ .string()
+ .min(1, ERROR_MESSAGES.required.sepNumber)
+ .optional(),
+
+ // File uploads
+ sepFile: z
+ .any()
+ .optional()
+ .refine((f) => !f || f instanceof File, { message: 'Harus berupa file yang valid' })
+ .refine((f) => !f || ACCEPTED_UPLOAD_TYPES.includes(f.type), {
+ message: 'Format file harus JPG, PNG, atau PDF',
+ })
+ .refine((f) => !f || f.size <= 1 * 1024 * 1024, { message: 'Maksimal 1MB' }),
+ sippFile: z
+ .any()
+ .optional()
+ .refine((f) => !f || f instanceof File, { message: 'Harus berupa file yang valid' })
+ .refine((f) => !f || ACCEPTED_UPLOAD_TYPES.includes(f.type), {
+ message: 'Format file harus JPG, PNG, atau PDF',
+ })
+ .refine((f) => !f || f.size <= 1 * 1024 * 1024, { message: 'Maksimal 1MB' }),
+ })
+ .refine(
+ (data) => {
+ // If payment type is jkn, then patient category is required
+ if (data.paymentType === 'jkn') {
+ return data.patientCategory && data.patientCategory.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.patientCategory,
+ path: ['patientCategory'],
+ },
+ )
+ .refine(
+ (data) => {
+ // If payment type is jkn, then card number is required
+ if (data.paymentType === 'jkn') {
+ return data.cardNumber && data.cardNumber.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.cardNumber,
+ path: ['cardNumber'],
+ },
+ )
+ .refine(
+ (data) => {
+ // If payment type is jkn, then SEP type is required
+ if (data.paymentType === 'jkn') {
+ return data.sepType && data.sepType.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.sepType,
+ path: ['sepType'],
+ },
+ )
+ .refine(
+ (data) => {
+ // If payment type is jkn and SEP type is selected, then SEP number is required
+ if (data.paymentType === 'jkn' && data.sepType && data.sepType.trim() !== '') {
+ return data.sepNumber && data.sepNumber.trim() !== ''
+ }
+ return true
+ },
+ {
+ message: ERROR_MESSAGES.required.sepNumber,
+ path: ['sepNumber'],
+ },
+ )
+
+type IntegrationEncounterFormData = z.infer
+
+export { IntegrationEncounterSchema }
+export type { IntegrationEncounterFormData }
+
diff --git a/app/services/doctor.service.ts b/app/services/doctor.service.ts
index 64ab22e9..74104c2c 100644
--- a/app/services/doctor.service.ts
+++ b/app/services/doctor.service.ts
@@ -1,9 +1,11 @@
// Base
import * as base from './_crud-base'
-import type { Doctor } from "~/models/doctor";
+
+// Types
+import type { Doctor } from '~/models/doctor'
const path = '/api/v1/doctor'
-const name = 'device'
+const name = 'doctor'
export function create(data: any) {
return base.create(path, data, name)
@@ -31,8 +33,8 @@ export async function getValueLabelList(params: any = null): Promise<{ value: st
if (result.success) {
const resultData = result.body?.data || []
data = resultData.map((item: Doctor) => ({
- value: item.id,
- label: item.employee.person.name,
+ value: item.id ? String(item.id) : '',
+ label: item.employee?.person?.name || '',
}))
}
return data
diff --git a/app/services/patient.service.ts b/app/services/patient.service.ts
index 7591f356..6a7742c3 100644
--- a/app/services/patient.service.ts
+++ b/app/services/patient.service.ts
@@ -40,6 +40,22 @@ export async function getPatientDetail(id: number) {
}
}
+export async function getPatientByIdentifier(search: string) {
+ try {
+ const urlPath = search.length === 16 ? `by-resident-identity/search=${encodeURIComponent(search)}` : `/search/${search}`
+ const url = `${mainUrl}/${urlPath}`
+ const resp = await xfetch(url, 'GET')
+ const result: any = {}
+ result.success = resp.success
+ result.body = (resp.body as Record) || {}
+ result.type = urlPath.includes('by-resident-identity') ? 'resident-identity' : 'identity'
+ return result
+ } catch (error) {
+ console.error('Error fetching patient by identifier:', error)
+ throw new Error('Failed to get patient by identifier')
+ }
+}
+
export async function postPatient(record: any) {
try {
const resp = await xfetch(mainUrl, 'POST', record)
diff --git a/app/services/specialist.service.ts b/app/services/specialist.service.ts
index ee955421..b18eac34 100644
--- a/app/services/specialist.service.ts
+++ b/app/services/specialist.service.ts
@@ -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[], byCode = true): TreeItem[] {
+ return specialists.map((specialist: Specialist) => ({
+ value: byCode ? String(specialist.code) : String(specialist.id),
+ 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,
+ }))
+}
diff --git a/app/services/vclaim-control-letter.service.ts b/app/services/vclaim-control-letter.service.ts
new file mode 100644
index 00000000..007e91c5
--- /dev/null
+++ b/app/services/vclaim-control-letter.service.ts
@@ -0,0 +1,29 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim-swagger/RencanaKontrol'
+const name = 'rencana-kontrol'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.letterNumber && params.mode === 'by-control') {
+ url += `/noSuratKontrol/${params.letterNumber}`
+ }
+ if (params?.letterNumber && params.mode === 'by-card') {
+ url += `/noka/${params.letterNumber}`
+ }
+ if (params?.letterNumber && params.mode === 'by-sep') {
+ url += `/${params.letterNumber}`
+ }
+ if (params?.letterNumber && params.mode === 'by-schedule') {
+ url += `/jadwalDokter?jeniskontrol=${params.controlType}&kodepoli=${params.poliCode}&tanggalkontrol=${params.controlDate}`
+ delete params.controlType
+ delete params.poliCode
+ delete params.controlDate
+ }
+ if (params) {
+ delete params.letterNumber
+ delete params.mode
+ }
+ return base.getList(url, params, name)
+}
\ No newline at end of file
diff --git a/app/services/vclaim-diagnose-referral.service.ts b/app/services/vclaim-diagnose-referral.service.ts
new file mode 100644
index 00000000..34b94780
--- /dev/null
+++ b/app/services/vclaim-diagnose-referral.service.ts
@@ -0,0 +1,9 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/diagnose-prb'
+const name = 'diagnose-referral'
+
+export function getList(params: any = null) {
+ return base.getList(path, params, name)
+}
diff --git a/app/services/vclaim-diagnose.service.ts b/app/services/vclaim-diagnose.service.ts
new file mode 100644
index 00000000..a676f516
--- /dev/null
+++ b/app/services/vclaim-diagnose.service.ts
@@ -0,0 +1,28 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/diagnose'
+const name = 'diagnose'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params && params?.diagnosa) {
+ url += `/${params.diagnosa}`
+ delete params.diagnosa
+ }
+ return base.getList(url, params, 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?.response?.diagnosa || []
+ const resultUnique = [...new Map(resultData.map((item: any) => [item.kode, item])).values()]
+ data = resultUnique.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: `${item.kode} - ${item.nama}`,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-doctor.service.ts b/app/services/vclaim-doctor.service.ts
new file mode 100644
index 00000000..0d5ec9dc
--- /dev/null
+++ b/app/services/vclaim-doctor.service.ts
@@ -0,0 +1,36 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/responsible-doctor'
+const name = 'responsible-doctor'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.serviceType) {
+ url += `/${params.serviceType}`
+ delete params.serviceType
+ }
+ if (params?.serviceDate) {
+ url += `/${params.serviceDate}`
+ delete params.serviceDate
+ }
+ if (params?.specialistCode || (Number(params.specialistCode) === 0)) {
+ url += `/${params.specialistCode}`
+ delete params.specialistCode
+ }
+ return base.getList(url, params, 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?.response?.list || []
+ const resultUnique = [...new Map(resultData.map((item: any) => [item.kode, item])).values()]
+ data = resultUnique.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: `${item.kode} - ${item.nama}`,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-healthcare.service.ts b/app/services/vclaim-healthcare.service.ts
new file mode 100644
index 00000000..09cc0089
--- /dev/null
+++ b/app/services/vclaim-healthcare.service.ts
@@ -0,0 +1,32 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/healthcare'
+const name = 'healthcare'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.healthcare) {
+ url += `/${params.healthcare}`
+ delete params.healthcare
+ }
+ if (params?.healthcareType) {
+ url += `/${params.healthcareType}`
+ delete params.healthcareType
+ }
+ return base.getList(url, params, 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?.response?.faskes || []
+ const resultUnique = [...new Map(resultData.map((item: any) => [item.kode, item])).values()]
+ data = resultUnique.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: `${item.kode} - ${item.nama}`,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-medicine.service.ts b/app/services/vclaim-medicine.service.ts
new file mode 100644
index 00000000..15a42050
--- /dev/null
+++ b/app/services/vclaim-medicine.service.ts
@@ -0,0 +1,22 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/medicine'
+const name = 'medicine'
+
+export function getList(params: any = null) {
+ return base.getList(path, params, 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?.response?.list || []
+ data = resultData.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: item.nama,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-member.service.ts b/app/services/vclaim-member.service.ts
new file mode 100644
index 00000000..bbe054ec
--- /dev/null
+++ b/app/services/vclaim-member.service.ts
@@ -0,0 +1,21 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/member'
+const name = 'member'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.number && params.mode === 'by-identity') {
+ url += `/nik/${params.number}/${params.date}`
+ }
+ if (params?.number && params.mode === 'by-card') {
+ url += `/bpjs/${params.number}/${params.date}`
+ }
+ if (params) {
+ delete params.number
+ delete params.mode
+ delete params.date
+ }
+ return base.getList(url, params, name)
+}
diff --git a/app/services/vclaim-monitoring-history.service.ts b/app/services/vclaim-monitoring-history.service.ts
new file mode 100644
index 00000000..ac9f2113
--- /dev/null
+++ b/app/services/vclaim-monitoring-history.service.ts
@@ -0,0 +1,17 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/monitoring/hist'
+const name = 'monitoring-history'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params && params?.cardNumber) {
+ url += `/${params.cardNumber}/${params.startDate}/${params.endDate}`
+ delete params.cardNumber
+ delete params.startDate
+ delete params.endDate
+ }
+
+ return base.getList(url, params, name)
+}
diff --git a/app/services/vclaim-monitoring-visit.service.ts b/app/services/vclaim-monitoring-visit.service.ts
new file mode 100644
index 00000000..0c5da64e
--- /dev/null
+++ b/app/services/vclaim-monitoring-visit.service.ts
@@ -0,0 +1,71 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/monitoring/visit'
+const name = 'monitoring-visit'
+
+const dummyResponse = {
+ metaData: {
+ code: '200',
+ message: 'Sukses',
+ },
+ response: {
+ sep: [
+ {
+ diagnosa: 'K65.0',
+ jnsPelayanan: 'R.Inap',
+ kelasRawat: '2',
+ nama: 'HANIF ABDURRAHMAN',
+ noKartu: '0001819122189',
+ noSep: '0301R00110170000004',
+ noRujukan: '0301U01108180200084',
+ poli: null,
+ tglPlgSep: '2017-10-03',
+ tglSep: '2017-10-01',
+ },
+ {
+ diagnosa: 'I50.0',
+ jnsPelayanan: 'R.Inap',
+ kelasRawat: '3',
+ nama: 'ASRIZAL',
+ noKartu: '0002283324674',
+ noSep: '0301R00110170000005',
+ noRujukan: '0301U01108180200184',
+ poli: null,
+ tglPlgSep: '2017-10-10',
+ tglSep: '2017-10-01',
+ },
+ ],
+ },
+}
+
+export async function getList(params: any = null) {
+ try {
+ let url = path
+ if (params?.date && params.serviceType) {
+ url += `/${params.date}/${params.serviceType}`
+ }
+ if (params) {
+ delete params.date
+ delete params.serviceType
+ }
+ const resp = await base.getList(url, params, name)
+
+ // Jika success false, return dummy response
+ if (!resp.success || !resp.body?.response) {
+ return {
+ success: true,
+ body: dummyResponse,
+ }
+ }
+
+ return resp
+ } catch (error) {
+ // Jika terjadi error, return dummy response
+ console.error(`Error fetching ${name}s:`, error)
+ return {
+ success: true,
+ body: dummyResponse,
+ }
+ }
+}
diff --git a/app/services/vclaim-reference-hospital-letter.service.ts b/app/services/vclaim-reference-hospital-letter.service.ts
new file mode 100644
index 00000000..115f6558
--- /dev/null
+++ b/app/services/vclaim-reference-hospital-letter.service.ts
@@ -0,0 +1,16 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim-swagger/Rujukan/RS'
+const name = 'rujukan-rumah-sakit'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.letterNumber) {
+ url += `/${params.letterNumber}`
+ }
+ if (params) {
+ delete params.letterNumber
+ }
+ return base.getList(url, params, name)
+}
\ No newline at end of file
diff --git a/app/services/vclaim-reference-letter.service.ts b/app/services/vclaim-reference-letter.service.ts
new file mode 100644
index 00000000..0a721d19
--- /dev/null
+++ b/app/services/vclaim-reference-letter.service.ts
@@ -0,0 +1,16 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim-swagger/RujukanKhusus'
+const name = 'rujukan-khusus'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.letterNumber) {
+ url += `?noRujukan=${params.letterNumber}`
+ }
+ if (params) {
+ delete params.letterNumber
+ }
+ return base.getList(url, params, name)
+}
\ No newline at end of file
diff --git a/app/services/vclaim-region-city.service.ts b/app/services/vclaim-region-city.service.ts
new file mode 100644
index 00000000..4ebfca20
--- /dev/null
+++ b/app/services/vclaim-region-city.service.ts
@@ -0,0 +1,27 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/regency'
+const name = 'cities'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.province) {
+ url += `/${params.province}`
+ delete params.province
+ }
+ return base.getList(url, params, 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?.response?.list || []
+ data = resultData.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: item.nama,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-region-district.service.ts b/app/services/vclaim-region-district.service.ts
new file mode 100644
index 00000000..0b0585a8
--- /dev/null
+++ b/app/services/vclaim-region-district.service.ts
@@ -0,0 +1,27 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/district'
+const name = 'districts'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.city) {
+ url += `/${params.city}`
+ delete params.city
+ }
+ return base.getList(url, params, 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?.response?.list || []
+ data = resultData.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: item.nama,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-region-province.service.ts b/app/services/vclaim-region-province.service.ts
new file mode 100644
index 00000000..5d58bb26
--- /dev/null
+++ b/app/services/vclaim-region-province.service.ts
@@ -0,0 +1,22 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/province'
+const name = 'provinces'
+
+export function getList(params: any = null) {
+ return base.getList(path, params, 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?.response?.list || []
+ data = resultData.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: item.nama,
+ }))
+ }
+ return data
+}
diff --git a/app/services/vclaim-sep.service.ts b/app/services/vclaim-sep.service.ts
new file mode 100644
index 00000000..fdccc9c4
--- /dev/null
+++ b/app/services/vclaim-sep.service.ts
@@ -0,0 +1,95 @@
+// Base
+import * as base from './_crud-base'
+
+// Types
+import type { IntegrationBpjsFormData } from '~/schemas/integration-bpjs.schema'
+
+const path = '/api/vclaim-swagger/sep'
+const name = 'sep'
+
+export function create(data: any) {
+ return base.create(path, data, name)
+}
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.number) {
+ url += `/${params.number}`
+ delete params.number
+ }
+ return base.getList(url, params, name)
+}
+
+export function makeSepData(
+ data: IntegrationBpjsFormData & {
+ referralFrom?: string
+ referralTo?: string
+ referralLetterDate?: string
+ referralLetterNumber?: string
+ },
+) {
+ const content = {
+ noKartu: data.cardNumber || '',
+ tglSep: data.sepDate,
+ ppkPelayanan: data.fromClinic || '',
+ jnsPelayanan: data.admissionType ? String(data.admissionType) : '1',
+ noMR: data.medicalRecordNumber || '',
+ catatan: data.note || '',
+ diagAwal: data.initialDiagnosis || '',
+ poli: {
+ tujuan: data.destinationClinic || '',
+ eksekutif: data.clinicExcecutive === 'yes' ? '1' : '0',
+ },
+ cob: {
+ cob: data.cob === 'yes' ? '1' : '0',
+ },
+ katarak: {
+ katarak: data.cataract === 'yes' ? '1' : '0',
+ },
+ tujuanKunj: data.purposeOfVisit || '',
+ flagProcedure: data.procedureType || '',
+ kdPenunjang: data.supportCode || '',
+ assesmentPel: data.serviceAssessment || '',
+ skdp: {
+ noSurat: ['3'].includes(data.admissionType) ? data.referralLetterNumber : '',
+ kodeDPJP: ['3'].includes(data.admissionType)? data.attendingDoctor : '',
+ },
+ rujukan: {
+ asalRujukan: ['2'].includes(data.admissionType) ? data?.referralFrom || '' : '',
+ tglRujukan: ['2'].includes(data.admissionType) ? data?.referralLetterDate || '' : '',
+ noRujukan: ['2'].includes(data.admissionType) ? data?.referralLetterNumber || '' : '',
+ ppkRujukan: ['2'].includes(data.admissionType) ? data?.referralTo || '' : '',
+ },
+ klsRawat: {
+ klsRawatHak: data.classLevel || '',
+ klsRawatNaik: data.classLevelUpgrade || '',
+ pembiayaan: data.classPaySource || '',
+ penanggungJawab: data.responsiblePerson || '',
+ },
+ dpjpLayan: data.attendingDoctor || '',
+ noTelp: data.phoneNumber || '',
+ user: data.patientName || '',
+ jaminan: {
+ lakaLantas: data.trafficAccident || '0',
+ noLP: data.lpNumber || '',
+ penjamin: {
+ tglKejadian: data.accidentDate || '',
+ keterangan: data.accidentNote || '',
+ suplesi: {
+ suplesi: data.suplesi === 'yes' ? '1' : '0',
+ noSepSuplesi: data.suplesiNumber || '',
+ lokasiLaka: {
+ kdPropinsi: data.accidentProvince || '',
+ kdKabupaten: data.accidentCity || '',
+ kdKecamatan: data.accidentDistrict || '',
+ },
+ },
+ },
+ },
+ }
+ return {
+ request: {
+ t_sep: content,
+ },
+ }
+}
diff --git a/app/services/vclaim-unit.service.ts b/app/services/vclaim-unit.service.ts
new file mode 100644
index 00000000..970a3f00
--- /dev/null
+++ b/app/services/vclaim-unit.service.ts
@@ -0,0 +1,28 @@
+// Base
+import * as base from './_crud-base'
+
+const path = '/api/vclaim/v1/reference/unit'
+const name = 'unit'
+
+export function getList(params: any = null) {
+ let url = path
+ if (params?.unitCode) {
+ url += `/${params.unitCode}`
+ delete params.unitCode
+ }
+ return base.getList(url, params, 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?.response?.faskes || []
+ const resultUnique = [...new Map(resultData.map((item: any) => [item.kode, item])).values()]
+ data = resultUnique.map((item: any) => ({
+ value: item.kode ? String(item.kode) : '',
+ label: `${item.kode} - ${item.nama}`,
+ }))
+ }
+ return data
+}
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 55cdb6bf..f4f0b231 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -5,8 +5,12 @@ export default defineNuxtConfig({
devtools: { enabled: true },
runtimeConfig: {
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
+ VCLAIM: process.env.NUXT_API_VCLAIM || 'http://localhost:3000',
+ VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
public: {
API_ORIGIN: process.env.NUXT_API_ORIGIN || 'http://localhost:3000',
+ VCLAIM: process.env.NUXT_API_VCLAIM || 'http://localhost:3000',
+ VCLAIM_SWAGGER: process.env.NUXT_API_VCLAIM_SWAGGER || 'http://localhost:3000',
},
},
ssr: false,
diff --git a/server/api/[...req].ts b/server/api/[...req].ts
index 8404cc2a..5948eda6 100644
--- a/server/api/[...req].ts
+++ b/server/api/[...req].ts
@@ -5,11 +5,20 @@ export default defineEventHandler(async (event) => {
const headers = getRequestHeaders(event)
const url = getRequestURL(event)
const config = useRuntimeConfig()
-
- const apiOrigin = config.public.API_ORIGIN
- const pathname = url.pathname.replace(/^\/api/, '')
- const targetUrl = apiOrigin + pathname + (url.search || '')
+ const apiOrigin = config.public.API_ORIGIN
+ const apiVclaim = config.public.VCLAIM
+ const apiVclaimSwagger = config.public.VCLAIM_SWAGGER
+ const pathname = url.pathname.replace(/^\/api/, '')
+ const isVclaim = pathname.includes('/vclaim')
+
+ let targetUrl = apiOrigin + pathname + (url.search || '')
+ if (pathname.includes('/vclaim')) {
+ targetUrl = apiVclaim + pathname.replace('/vclaim', '') + (url.search || '')
+ }
+ if (pathname.includes('/vclaim-swagger')) {
+ targetUrl = apiVclaimSwagger + pathname.replace('/vclaim-swagger', '') + (url.search || '')
+ }
const verificationId = headers['verification-id'] as string | undefined
let bearer = ''
@@ -21,8 +30,10 @@ export default defineEventHandler(async (event) => {
}
const forwardHeaders = new Headers()
- if (headers['content-type']) forwardHeaders.set('Content-Type', headers['content-type'])
- forwardHeaders.set('Authorization', `Bearer ${bearer}`)
+ if (!isVclaim) {
+ if (headers['content-type']) forwardHeaders.set('Content-Type', headers['content-type'])
+ forwardHeaders.set('Authorization', `Bearer ${bearer}`)
+ }
let body: any
if (['POST', 'PATCH'].includes(method!)) {
@@ -41,5 +52,15 @@ export default defineEventHandler(async (event) => {
body,
})
+ if (isVclaim) {
+ const resClone = res.clone()
+ const responseBody = await resClone.json()
+ return {
+ status: resClone.status,
+ headers: resClone.headers,
+ ...responseBody,
+ }
+ }
+
return res
})