final update
This commit is contained in:
+1
-1
@@ -377,7 +377,7 @@ const loadCarouselItems = async () => {
|
||||
);
|
||||
|
||||
carouselItems.value = itemsWithRatio;
|
||||
console.log('Carousel items loaded:', carouselItems.value);
|
||||
// console.log('Carousel items loaded:', carouselItems.value);
|
||||
} catch (error) {
|
||||
console.error('Error loading carousel items:', error);
|
||||
} finally {
|
||||
|
||||
@@ -48,46 +48,45 @@
|
||||
</div>
|
||||
|
||||
<!-- Payment Details Summary -->
|
||||
<div class="payment-summary" v-if="paymentStore.patientInfo">
|
||||
<div class="summary-card">
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Nomor RM:</span>
|
||||
<span class="summary-value">{{
|
||||
paymentStore.qrData.display_norm || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="payment-summary" v-if="paymentStore.patientInfo">
|
||||
<div class="summary-card">
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Nomor RM:</span>
|
||||
<span class="summary-value">{{
|
||||
paymentStore.qrData.display_norm || "-"
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">No. Billing:</span>
|
||||
<span class="summary-value">{{
|
||||
paymentStore.qrData.display_nobill || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">No. Billing:</span>
|
||||
<span class="summary-value">{{
|
||||
paymentStore.qrData.display_nobill || "-"
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Pasien:</span>
|
||||
<span class="summary-value">{{
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Pasien:</span>
|
||||
<span class="summary-value">{{
|
||||
paymentStore.patientInfo.name
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Nominal:</span>
|
||||
<span class="summary-value amount">{{
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Nominal:</span>
|
||||
<span class="summary-value amount">{{
|
||||
formatCurrency(paymentStore.patientInfo.amount)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Status:</span>
|
||||
<v-chip color="success" variant="flat" size="small">
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
<span class="summary-label">Status:</span>
|
||||
<v-chip color="success" variant="flat" size="small">
|
||||
<v-icon size="14" class="mr-1">mdi-check</v-icon>
|
||||
Berhasil
|
||||
</v-chip>
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -292,5 +291,10 @@ const formatCurrency = (amountString) => {
|
||||
.success-container {
|
||||
max-width: 650px;
|
||||
}
|
||||
|
||||
.logo-section-contact {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -159,7 +159,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<!-- <div class="action-buttons">
|
||||
<v-btn
|
||||
color="grey-lighten-1"
|
||||
variant="outlined"
|
||||
@@ -192,7 +192,7 @@
|
||||
<v-icon class="mr-2">mdi-close-circle-outline</v-icon>
|
||||
Simulasi Pending
|
||||
</v-btn>
|
||||
</div>
|
||||
</div> -->
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -124,6 +124,39 @@ export function useFileSync() {
|
||||
|
||||
}
|
||||
|
||||
// const tampilFileBrowser = async () => {
|
||||
|
||||
// await syncFiles();
|
||||
// if (isSyncing.value) {
|
||||
// try {
|
||||
// const localStoreKeys = await localStore.keys()
|
||||
// // console.log('localStore Keys:', localStoreKeys)
|
||||
// const files: { name: string; blob: Blob; type: string; url: string }[] = []
|
||||
|
||||
// for (const key of localStoreKeys) {
|
||||
// if (!key.endsWith('-meta')) {
|
||||
// const fileBlob = await localStore.getItem<Blob>(key)
|
||||
|
||||
// if (fileBlob instanceof Blob) {
|
||||
// const url = URL.createObjectURL(fileBlob)
|
||||
// files.push({ name: key, blob: fileBlob, type: fileBlob.type, url: url })
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // console.log('Files in IndexedDB:',fileLocal.value)
|
||||
// return fileLocal.value = files
|
||||
|
||||
// } catch (err) {
|
||||
// console.error('Gagal memuat file dari IndexedDB:', err)
|
||||
// } finally {
|
||||
// isSyncing.value = false
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
const tampilFileBrowser = async () => {
|
||||
|
||||
await syncFiles();
|
||||
@@ -144,7 +177,16 @@ export function useFileSync() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log('Files in IndexedDB:',fileLocal.value)
|
||||
// console.log('Files in IndexedDB:',files)
|
||||
// console.log('File Server:', fileServer.value)
|
||||
const fileTidaksama = files.filter(local => !fileServer.value.some(fs => fs.name === local.name))
|
||||
// console.log('File tidak sama dengan server:', fileTidaksama)
|
||||
for (const namaFile of fileTidaksama) {
|
||||
const firstNamePart = namaFile.name.split('.')[0];
|
||||
console.log('file lokal yang tidak ada di server:', firstNamePart);
|
||||
await removeFiles(firstNamePart)
|
||||
}
|
||||
|
||||
return fileLocal.value = files
|
||||
|
||||
} catch (err) {
|
||||
@@ -156,7 +198,6 @@ export function useFileSync() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Bersihkan object URL biar gak bocor memori
|
||||
onBeforeUnmount(() => {
|
||||
fileLocal.value.forEach(f => URL.revokeObjectURL(f.url))
|
||||
|
||||
+13
-1
@@ -2,7 +2,19 @@
|
||||
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
devtools: {
|
||||
enabled: false, // Enable Nuxt DevTools untuk debugging
|
||||
timeline: {
|
||||
enabled: false, // Enable timeline untuk performance monitoring
|
||||
},
|
||||
},
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
folder_path: "\\\\10.10.123.49\\qris",
|
||||
url_base: "ws://10.10.150.222:8080/api/v1/ws",
|
||||
query: "user_id=QRIS&room=BANKJATIM"
|
||||
}
|
||||
},
|
||||
ssr:false,
|
||||
modules: [
|
||||
'@nuxt/content',
|
||||
|
||||
+3
-2
@@ -47,9 +47,10 @@ import { useWebSocket } from "~/composables/useWebSocket";
|
||||
const paymentStore = usePaymentStore();
|
||||
const router = useRouter();
|
||||
const localIpAddress = ref(null);
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const WS_URL_BASE = "ws://10.10.150.45:8080/api/v1/ws";
|
||||
const WS_QUERY = "user_id=QRIS&room=BANKJATIM";
|
||||
const WS_URL_BASE = config.public.url_base;
|
||||
const WS_QUERY = config.public.query;
|
||||
const fullWsUrl = `${WS_URL_BASE}?${WS_QUERY}`;
|
||||
|
||||
const {
|
||||
|
||||
@@ -2,8 +2,8 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { lookup } from 'mime-types'
|
||||
import { createError, setHeader, sendStream } from 'h3'
|
||||
|
||||
const FOLDER_PATH = '\\\\10.10.150.129\\Shared'
|
||||
const config = useRuntimeConfig()
|
||||
const FOLDER_PATH = config.public.folder_path;
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const rawName = event.context.params?.name as string | undefined
|
||||
|
||||
@@ -5,9 +5,11 @@ import { promisify } from "util"
|
||||
|
||||
const readdir = promisify(fs.readdir)
|
||||
const stat = promisify(fs.stat)
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
// Lokasi folder network share (pastikan sudah di-mount / accessible dari server)
|
||||
const FOLDER_PATH = "\\\\10.10.150.129\\Shared"
|
||||
// const FOLDER_PATH = "\\\\10.10.123.49\\qris"
|
||||
const FOLDER_PATH = config.public.folder_path;
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
|
||||
+98
-93
@@ -1,68 +1,68 @@
|
||||
// src/stores/payment.ts
|
||||
import { defineStore } from 'pinia';
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
// 1. Definisikan Tipe Data untuk qrData (penting untuk properti expired_at yang di-spread)
|
||||
// definisi tipe data
|
||||
interface QrData {
|
||||
qrvalue: string | null;
|
||||
display_nobill: string | null;
|
||||
display_name: string | null;
|
||||
display_amount: string | null;
|
||||
status: '1' | '2' | '0' | null; // "1": Pending, "2": Sukses, "0": Gagal/Expired
|
||||
ip: string | null;
|
||||
// Tambahkan properti yang di-spread dari API, termasuk expired_at
|
||||
expired_at?: string; // Tambahkan expired_at sebagai properti opsional
|
||||
[key: string]: any; // Indeks signature untuk properti dinamis (misal: transaction_id, reason)
|
||||
qrvalue: string | null;
|
||||
display_nobill: string | null;
|
||||
display_name: string | null;
|
||||
display_amount: string | null;
|
||||
status: "1" | "2" | "0" | null; // "1": Pending, "2": Sukses, "0": Gagal/Pending
|
||||
ip: string | null;
|
||||
expired_at?: string;
|
||||
[key: string]: any; // Indeks signature untuk properti dinamis (misal: transaction_id, reason)
|
||||
}
|
||||
|
||||
// 2. Definisikan Tipe Data untuk State
|
||||
// definisi tipe data untuk State
|
||||
interface PaymentState {
|
||||
currentStep: 1 | 2 | 3 | 4;
|
||||
patientInfo: {
|
||||
name: string;
|
||||
amount: string;
|
||||
expiry: string;
|
||||
};
|
||||
qrData: QrData;
|
||||
currentStep: 1 | 2 | 3 | 4;
|
||||
patientInfo: {
|
||||
name: string;
|
||||
amount: string;
|
||||
expiry: string;
|
||||
};
|
||||
qrData: QrData;
|
||||
}
|
||||
|
||||
// Variabel di luar store untuk menyimpan timer, agar persistensi di luar state Pinia
|
||||
let autoResetTimer: number | null = null;
|
||||
let autoResetTimer: number | null = null;
|
||||
|
||||
export const usePaymentStore = defineStore('payment', {
|
||||
state: (): PaymentState => ({ // Gunakan tipedata PaymentState
|
||||
currentStep: 1, // 1: Menunggu Data, 2: Tampilkan QRIS, 3: Sukses, 4: Gagal
|
||||
export const usePaymentStore = defineStore("payment", {
|
||||
state: (): PaymentState => ({
|
||||
currentStep: 1, // 1: Menunggu Data, 2: Tampilkan QRIS, 3: Sukses, 4: Gagal/Pending
|
||||
patientInfo: {
|
||||
name: '',
|
||||
amount: '',
|
||||
expiry: '', // Waktu kedaluwarsa diambil dari qrData (via spread)
|
||||
name: "",
|
||||
amount: "",
|
||||
expiry: "",
|
||||
},
|
||||
qrData: {
|
||||
qrvalue: null,
|
||||
display_nobill: null,
|
||||
display_name: null,
|
||||
display_amount: null,
|
||||
status: null,
|
||||
status: null,
|
||||
ip: null,
|
||||
// created_at DIHAPUS
|
||||
} as QrData, // Type casting untuk QrData
|
||||
} as QrData,
|
||||
}),
|
||||
|
||||
|
||||
getters: {
|
||||
hasQrData: (state): boolean => !!state.qrData.qrvalue,
|
||||
safeQrValue: (state): string => state.qrData.qrvalue || 'https://www.google.com',
|
||||
|
||||
safeQrValue: (state): string =>
|
||||
state.qrData.qrvalue || "https://www.google.com",
|
||||
|
||||
isReadyForStatusCheck: (state): boolean => {
|
||||
return !!(state.qrData.display_nobill && state.qrData.display_name);
|
||||
},
|
||||
|
||||
|
||||
debugInfo: (state) => ({
|
||||
step: state.currentStep,
|
||||
hasIdentifiers: !!(state.qrData.display_nobill && state.qrData.display_name),
|
||||
hasIdentifiers: !!(
|
||||
state.qrData.display_nobill && state.qrData.display_name
|
||||
),
|
||||
qrData: state.qrData,
|
||||
patientInfo: state.patientInfo,
|
||||
})
|
||||
}),
|
||||
},
|
||||
|
||||
|
||||
actions: {
|
||||
nextStep() {
|
||||
this.currentStep++;
|
||||
@@ -70,32 +70,31 @@ export const usePaymentStore = defineStore('payment', {
|
||||
prevStep() {
|
||||
this.currentStep--;
|
||||
},
|
||||
|
||||
|
||||
setStep(step: 1 | 2 | 3 | 4) {
|
||||
console.log(`[STORE] Setting step from ${this.currentStep} to ${step}`);
|
||||
this.currentStep = step;
|
||||
},
|
||||
|
||||
startAutoReset() {
|
||||
if (autoResetTimer) {
|
||||
clearTimeout(autoResetTimer);
|
||||
}
|
||||
console.log('🏠 [STORE] Starting auto return timer (50s) for reset.');
|
||||
|
||||
// Di TypeScript, setTimeout mengembalikan number
|
||||
autoResetTimer = setTimeout(() => {
|
||||
console.log('🏠 [STORE] Auto return to home triggered');
|
||||
this.reset();
|
||||
}, 10000);
|
||||
if (autoResetTimer) {
|
||||
clearTimeout(autoResetTimer);
|
||||
}
|
||||
console.log("🏠 [STORE] Starting auto return timer (10s) for reset.");
|
||||
|
||||
autoResetTimer = setTimeout(() => {
|
||||
console.log("🏠 [STORE] Auto return to home triggered");
|
||||
this.reset();
|
||||
}, 10000);
|
||||
},
|
||||
|
||||
|
||||
reset() {
|
||||
console.log('--- [STORE] Resetting payment store ---');
|
||||
console.log("--- [STORE] Resetting payment store ---");
|
||||
this.currentStep = 1;
|
||||
this.patientInfo = {
|
||||
name: '',
|
||||
amount: '',
|
||||
expiry: '',
|
||||
name: "",
|
||||
amount: "",
|
||||
expiry: "",
|
||||
};
|
||||
this.qrData = {
|
||||
qrvalue: null,
|
||||
@@ -105,40 +104,43 @@ export const usePaymentStore = defineStore('payment', {
|
||||
status: null,
|
||||
ip: null,
|
||||
} as QrData; // Pastikan tipe kembali ke QrData
|
||||
if (autoResetTimer) {
|
||||
clearTimeout(autoResetTimer);
|
||||
autoResetTimer = null;
|
||||
}
|
||||
if (autoResetTimer) {
|
||||
clearTimeout(autoResetTimer);
|
||||
autoResetTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// Definisikan tipe untuk apiResponse
|
||||
updatePayment(apiResponse: { data: QrData[] }) {
|
||||
console.log("=== [STORE] UPDATE PAYMENT ===");
|
||||
|
||||
|
||||
if (!apiResponse?.data || apiResponse.data.length === 0) {
|
||||
console.error("Invalid API response structure or empty data array");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const apiData = apiResponse.data[0];
|
||||
const oldStatus = this.qrData.status;
|
||||
|
||||
|
||||
// Update qrData dengan semua properti dari API
|
||||
this.qrData = {
|
||||
qrvalue: apiData.qrvalue || apiData.qr_code || this.qrData.qrvalue,
|
||||
display_nobill: apiData.display_nobill || this.qrData.display_nobill,
|
||||
display_name: apiData.display_name || this.qrData.display_name,
|
||||
display_amount: apiData.display_amount || apiData.nominal || this.qrData.display_amount,
|
||||
status: apiData.status as QrData['status'] || this.qrData.status, // Type casting untuk status
|
||||
display_amount:
|
||||
apiData.display_amount ||
|
||||
apiData.nominal ||
|
||||
this.qrData.display_amount,
|
||||
status: (apiData.status as QrData["status"]) || this.qrData.status, // Type casting untuk status
|
||||
ip: apiData.ip || this.qrData.ip,
|
||||
// created_at DIHAPUS
|
||||
...apiData // expired_at akan masuk di sini
|
||||
|
||||
...apiData, // expired_at akan masuk di sini
|
||||
};
|
||||
|
||||
|
||||
// Update patientInfo
|
||||
this.patientInfo = {
|
||||
name: this.qrData.display_name || this.patientInfo.name || 'Unknown',
|
||||
amount: this.qrData.display_amount || this.patientInfo.amount || '0',
|
||||
name: this.qrData.display_name || this.patientInfo.name || "Unknown",
|
||||
amount: this.qrData.display_amount || this.patientInfo.amount || "0",
|
||||
expiry: this.patientInfo.expiry,
|
||||
};
|
||||
|
||||
@@ -150,22 +152,26 @@ export const usePaymentStore = defineStore('payment', {
|
||||
}
|
||||
|
||||
if (this.currentStep === 2 && oldStatus !== newStatus) {
|
||||
if (newStatus === "2") {
|
||||
console.log('🎉 [STORE] Status 2 (SUKSES) diterima. Pindah ke Step 3.');
|
||||
this.currentStep = 3;
|
||||
this.startAutoReset();
|
||||
} else if (newStatus === "0") {
|
||||
console.log('❌ [STORE] Status 0 (GAGAL) diterima. Pindah ke Step 4.');
|
||||
this.currentStep = 4;
|
||||
this.startAutoReset();
|
||||
}
|
||||
if (newStatus === "2") {
|
||||
console.log(
|
||||
"🎉 [STORE] Status 2 (SUKSES) diterima. Pindah ke Step 3."
|
||||
);
|
||||
this.currentStep = 3;
|
||||
this.startAutoReset();
|
||||
} else if (newStatus === "0") {
|
||||
console.log(
|
||||
"❌ [STORE] Status 0 (GAGAL) diterima. Pindah ke Step 4."
|
||||
);
|
||||
this.currentStep = 4;
|
||||
this.startAutoReset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log("Updated qrData:", this.qrData);
|
||||
console.log("Updated patientInfo:", this.patientInfo);
|
||||
console.log("===================");
|
||||
},
|
||||
|
||||
|
||||
// Method untuk debug
|
||||
debugCurrentState() {
|
||||
console.log("=== PAYMENT STORE DEBUG ===");
|
||||
@@ -177,30 +183,29 @@ export const usePaymentStore = defineStore('payment', {
|
||||
console.log("Safe QR Value:", this.safeQrValue);
|
||||
console.log("==========================");
|
||||
},
|
||||
|
||||
|
||||
// Method untuk validasi data
|
||||
validateData(): boolean {
|
||||
const issues: string[] = [];
|
||||
|
||||
|
||||
if (!this.qrData.display_nobill) {
|
||||
issues.push('display_nobill missing');
|
||||
issues.push("display_nobill missing");
|
||||
}
|
||||
|
||||
|
||||
if (!this.qrData.display_name) {
|
||||
issues.push('display_name missing');
|
||||
issues.push("display_name missing");
|
||||
}
|
||||
|
||||
// Perlu pengecekan properti qr_code pada apiData, bukan state
|
||||
if (!this.qrData.qrvalue) {
|
||||
issues.push('QR code missing');
|
||||
|
||||
if (!this.qrData.qrvalue) {
|
||||
issues.push("QR code missing");
|
||||
}
|
||||
|
||||
|
||||
if (issues.length > 0) {
|
||||
console.warn('Data validation issues:', issues);
|
||||
console.warn("Data validation issues:", issues);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user