diff --git a/app/components/content/sep/list.vue b/app/components/content/sep/list.vue
index 2fc1a7b4..a416528f 100644
--- a/app/components/content/sep/list.vue
+++ b/app/components/content/sep/list.vue
@@ -1,10 +1,5 @@
@@ -305,14 +116,14 @@ provide('table_data_loader', isLoading)
- Ekspor CSV
- Ekspor Excel
+ Ekspor CSV
+ Ekspor Excel
diff --git a/app/handlers/integration-sep-entry.handler.ts b/app/handlers/integration-sep-entry.handler.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/app/handlers/integration-sep-list.handler.ts b/app/handlers/integration-sep-list.handler.ts
new file mode 100644
index 00000000..7e989061
--- /dev/null
+++ b/app/handlers/integration-sep-list.handler.ts
@@ -0,0 +1,283 @@
+import { ref, reactive, watch } from 'vue'
+// Components
+import { toast } from '~/components/pub/ui/toast'
+// Types
+import type { Ref as VueRef } from 'vue'
+import type { DateRange } from 'radix-vue'
+import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
+import type { HeaderPrep, RefSearchNav } from '~/components/pub/my-ui/data/types'
+import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
+import type { VclaimSepData } from '~/models/vclaim'
+// Libraries
+import { CalendarDate, getLocalTimeZone } from '@internationalized/date'
+import { getFormatDateId } from '~/lib/date'
+import { downloadCsv, downloadXls } from '~/lib/download'
+import { serviceTypes } from '~/lib/constants.vclaim'
+import { getList as geMonitoringVisitList } from '~/services/vclaim-monitoring-visit.service'
+import { remove as removeSepData, makeSepDataForRemove } from '~/services/vclaim-sep.service'
+
+const headerKeys = [
+ 'letterDate',
+ 'letterNumber',
+ 'serviceType',
+ 'flow',
+ 'medicalRecordNumber',
+ 'patientName',
+ 'cardNumber',
+ 'controlLetterNumber',
+ 'controlLetterDate',
+ 'clinicDestination',
+ 'attendingDoctor',
+ 'diagnosis',
+ 'careClass',
+]
+
+const headerLabels = [
+ 'Tanggal SEP',
+ 'No. SEP',
+ 'Jenis Pelayanan',
+ 'Alur',
+ 'No. Rekam Medis',
+ 'Nama Pasien',
+ 'No. Kartu BPJS',
+ 'No. Surat Kontrol',
+ 'Tgl Surat Kontrol',
+ 'Poli Tujuan',
+ 'Dokter Penanggung Jawab',
+ 'Diagnosa',
+ 'Kelas Perawatan',
+]
+
+export function useIntegrationSepList() {
+ const userStore = useUserStore()
+ const today = new Date()
+ const initCalDate = (d: Date) => new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
+
+ const recId = ref(0)
+ const recAction = ref('')
+ const recItem = ref(null)
+
+ const data = ref([])
+ const dateSelection = ref({ start: initCalDate(today), end: initCalDate(today) }) as VueRef
+ const dateRange = ref(`${getFormatDateId(today)} - ${getFormatDateId(today)}`)
+ const serviceType = ref('2')
+ const serviceTypesList = ref([])
+ const search = ref('')
+ const open = ref(false)
+
+ const sepData = ref({
+ sepNumber: '',
+ cardNumber: '',
+ patientName: '',
+ })
+
+ const refSearchNav: RefSearchNav = {
+ onClick: () => {},
+ onInput: (_val: string) => {},
+ onClear: () => {},
+ }
+
+ const headerPrep: HeaderPrep = {
+ title: 'Daftar SEP Prosedur',
+ icon: 'i-lucide-panel-bottom',
+ addNav: {
+ label: 'Tambah',
+ onClick: () => {
+ navigateTo('/integration/bpjs/sep/add')
+ },
+ },
+ }
+
+ const paginationMeta = reactive({
+ recordCount: 0,
+ page: 1,
+ pageSize: 10,
+ totalPage: 5,
+ hasNext: false,
+ hasPrev: false,
+ })
+
+ const isLoading = reactive({
+ isTableLoading: false,
+ })
+
+ const getDateFilter = () => {
+ let dateFilter = ''
+ const isTimeLocal = true
+ const dateFirst =
+ dateSelection.value && dateSelection.value.start
+ ? dateSelection.value.start.toDate(getLocalTimeZone())
+ : new Date()
+ if (isTimeLocal && dateSelection.value && dateSelection.value.end) {
+ const { year, month, day } = dateSelection.value.end
+ dateFilter = `${year}-${month}-${day}`
+ } else {
+ dateFilter = dateFirst.toISOString().substring(0, 10)
+ }
+ return dateFilter
+ }
+
+ const getMonitoringVisitMappers = async () => {
+ isLoading.dataListLoading = true
+ data.value = []
+ const dateFilter = getDateFilter()
+ const result = await geMonitoringVisitList({
+ date: dateFilter || '',
+ serviceType: serviceType.value,
+ })
+
+ if (result && result.success && result.body) {
+ const visitsRaw = result.body?.response?.sep || []
+ if (!visitsRaw) {
+ isLoading.dataListLoading = false
+ return
+ }
+ visitsRaw.forEach((result: any) => {
+ let st = result.jnsPelayanan || '-'
+ if (st === 'R.Inap') st = 'Rawat Inap'
+ else if (st === '1' || st === 'R.Jalan') st = 'Rawat Jalan'
+
+ data.value.push({
+ letterDate: result.tglSep || '-',
+ letterNumber: result.noSep || '-',
+ serviceType: st,
+ flow: '-',
+ medicalRecordNumber: '-',
+ patientName: result.nama || '-',
+ cardNumber: result.noKartu || '-',
+ controlLetterNumber: result.noRujukan || '-',
+ controlLetterDate: result.tglPlgSep || '-',
+ clinicDestination: result.poli || '-',
+ attendingDoctor: '-',
+ diagnosis: result.diagnosa || '-',
+ careClass: result.kelasRawat || '-',
+ })
+ })
+ }
+
+ isLoading.dataListLoading = false
+ }
+
+ const getSepList = async () => {
+ await getMonitoringVisitMappers()
+ }
+
+ const setServiceTypes = () => {
+ serviceTypesList.value = Object.keys(serviceTypes).map((item) => ({
+ value: item.toString(),
+ label: serviceTypes[item],
+ })) as any
+ }
+
+ const setDateRange = () => {
+ const startCal = dateSelection.value.start
+ const endCal = dateSelection.value.end
+ const s = startCal ? startCal.toDate(getLocalTimeZone()) : today
+ const e = endCal ? endCal.toDate(getLocalTimeZone()) : today
+ dateRange.value = `${getFormatDateId(s)} - ${getFormatDateId(e)}`
+ }
+
+ const handleExportCsv = () => {
+ if (!data.value || data.value.length === 0) {
+ toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
+ return
+ }
+
+ const yyyy = today.getFullYear()
+ const mm = String(today.getMonth() + 1).padStart(2, '0')
+ const dd = String(today.getDate()).padStart(2, '0')
+ const dateStr = `${yyyy}-${mm}-${dd}`
+ const filename = `file-sep-${dateStr}.csv`
+ downloadCsv(headerKeys, headerLabels, data.value, filename, ',', true)
+ }
+
+ const handleExportExcel = async () => {
+ if (!data.value || data.value.length === 0) {
+ toast({ title: 'Kosong', description: 'Tidak ada data untuk diekspor', variant: 'destructive' })
+ return
+ }
+
+ const yyyy = today.getFullYear()
+ const mm = String(today.getMonth() + 1).padStart(2, '0')
+ const dd = String(today.getDate()).padStart(2, '0')
+ const dateStr = `${yyyy}-${mm}-${dd}`
+ const filename = `file-sep-${dateStr}.xlsx`
+ try {
+ await downloadXls(headerKeys, headerLabels, data.value, filename, 'SEP Data')
+ } catch (err: any) {
+ console.error('exportExcel error', err)
+ toast({ title: 'Gagal', description: err?.message || 'Gagal mengekspor data ke Excel', variant: 'destructive' })
+ }
+ }
+
+ const handleRowSelected = (row: any) => {
+ if (!row) return
+ sepData.value.sepNumber = row.letterNumber || ''
+ sepData.value.cardNumber = row.cardNumber || ''
+ sepData.value.patientName = row.patientName || ''
+ recItem.value = row
+ recId.value = (row && (row.id || row.recId)) || 0
+ }
+
+ const handlePageChange = (page: number) => {
+ // kept for compatibility
+ console.log('pageChange', page)
+ }
+
+ const handleRemove = async () => {
+ try {
+ const result = await removeSepData(
+ makeSepDataForRemove({ ...sepData.value, userName: userStore.user?.user_name }),
+ )
+ const backendMessage = result?.body?.message || result?.message || null
+ const backendStatus = result?.body?.status || result?.status || null
+
+ if (
+ backendMessage === 'success' ||
+ (backendStatus === 'error' && backendMessage === 'Decrypt failed: illegal base64 data at input byte 16')
+ ) {
+ await getSepList()
+ toast({ title: 'Berhasil', description: backendMessage || 'Data berhasil dihapus', variant: 'default' })
+ } else {
+ toast({ title: 'Gagal', description: backendMessage || 'Gagal menghapus data', variant: 'destructive' })
+ }
+ } catch (err: any) {
+ console.error('handleRemove error', err)
+ toast({
+ title: 'Gagal',
+ description: err?.message || 'Terjadi kesalahan saat menghapus data',
+ variant: 'destructive',
+ })
+ } finally {
+ open.value = false
+ }
+ }
+
+ return {
+ recId,
+ recAction,
+ recItem,
+ data,
+ dateSelection,
+ dateRange,
+ serviceType,
+ serviceTypesList,
+ search,
+ open,
+ sepData,
+ headerPrep,
+ refSearchNav,
+ paginationMeta,
+ isLoading,
+ getSepList,
+ setServiceTypes,
+ setDateRange,
+ handleExportCsv,
+ handleExportExcel,
+ handleRowSelected,
+ handlePageChange,
+ handleRemove,
+ }
+}
+
+export default useIntegrationSepList
diff --git a/app/lib/download.ts b/app/lib/download.ts
index 905f9fb3..eb996432 100644
--- a/app/lib/download.ts
+++ b/app/lib/download.ts
@@ -16,6 +16,7 @@
*/
export function downloadCsv(
headers: string[] | null,
+ headerLabels: string[],
data: Array | any[]>,
filename = 'data.csv',
delimiter = ',',
@@ -66,7 +67,7 @@ export function downloadCsv(
return Object.values(row).map(escape).join(delimiter)
})
- const headerRow = _headers ? _headers.join(delimiter) : null
+ const headerRow = headerLabels ? headerLabels.join(delimiter) : _headers ? _headers.join(delimiter) : null
const csvString = (addBOM ? '\uFEFF' : '') + [headerRow, ...rows].filter(Boolean).join('\r\n')
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' })
@@ -95,6 +96,7 @@ export function downloadCsv(
*/
export async function downloadXls(
headers: string[] | null,
+ headerLabels: string[],
data: Array | any[]>,
filename = 'data.xlsx',
sheetName = 'Sheet1',
@@ -136,8 +138,10 @@ export async function downloadXls(
return Object.values(row)
})
- // Combine headers and rows for sheet
- const sheetData = _headers ? [_headers, ...rows] : rows
+ // Combine headers/labels and rows for sheet
+ // If caller provided headerLabels (as display labels), prefer them.
+ const sheetHeader = headerLabels ? headerLabels : _headers ? _headers : null
+ const sheetData = sheetHeader ? [sheetHeader, ...rows] : rows
// Create worksheet and workbook
const ws = utils.aoa_to_sheet(sheetData)