update komponen, stores, desain
This commit is contained in:
No files matched your search
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<div class="medical-payment-app">
|
||||
<v-container fluid class="pa-0 full-height">
|
||||
<v-row justify="center" no-gutters class="full-height">
|
||||
<v-col cols="12" class="full-height">
|
||||
<v-card
|
||||
class="payment-card mx-auto"
|
||||
elevation="0"
|
||||
:class="{
|
||||
'contact-fullscreen': paymentStore.currentStep === 1,
|
||||
'payment-centered': paymentStore.currentStep === 2,
|
||||
'status-centered':
|
||||
paymentStore.currentStep === 3 ||
|
||||
paymentStore.currentStep === 4,
|
||||
}"
|
||||
>
|
||||
<component :is="activeComponent" v-if="hasIpAddress" />
|
||||
<div v-else class="text-center pa-10">
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
></v-progress-circular>
|
||||
<p class="mt-4">Memeriksa konfigurasi dan koneksi data...</p>
|
||||
<p v-if="hasIpAddress && wsIsConnected" class="mt-2 text-success">
|
||||
Koneksi WS: **Terhubung** (Siap menerima data 30s)
|
||||
</p>
|
||||
<p v-else-if="hasIpAddress" class="mt-2 text-warning">
|
||||
Koneksi WS: Menunggu/Terputus...
|
||||
</p>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { usePaymentStore } from "~/stores/payment";
|
||||
import Home from "~/components/Home.vue";
|
||||
import QRISPayment from "~/components/QRISPayment.vue";
|
||||
import Success from "~/components/PembayaranSukses.vue";
|
||||
import Gagal from "~/components/PembayaranGagal.vue";
|
||||
import { onMounted, onUnmounted, ref, computed, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useWebSocket } from "~/composables/useWebSocket";
|
||||
|
||||
const paymentStore = usePaymentStore();
|
||||
const router = useRouter();
|
||||
const localIpAddress = ref(null);
|
||||
|
||||
// **WEB SOCKET CONFIGURATION**
|
||||
const WS_URL_BASE = "ws://10.10.150.45:8084/api/v1/ws";
|
||||
const WS_QUERY = "user_id=QRIS&room=BANKJATIM";
|
||||
const fullWsUrl = `${WS_URL_BASE}?${WS_QUERY}`;
|
||||
|
||||
const {
|
||||
wsData,
|
||||
isConnected: wsIsConnected,
|
||||
connect: wsConnect,
|
||||
disconnect: wsDisconnect,
|
||||
} = useWebSocket(fullWsUrl);
|
||||
|
||||
const hasIpAddress = computed(() => !!localIpAddress.value);
|
||||
|
||||
const paymentSteps = {
|
||||
1: Home,
|
||||
2: QRISPayment,
|
||||
3: Success,
|
||||
4: Gagal,
|
||||
};
|
||||
|
||||
const activeComponent = computed(() => {
|
||||
return paymentSteps[paymentStore.currentStep] || Home;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => paymentStore.currentStep,
|
||||
(newStep, oldStep) => {
|
||||
console.log(`Step berubah dari ${oldStep} ke ${newStep}`);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
wsData,
|
||||
(newStatus) => {
|
||||
const payloadData = newStatus?.data?.data; // Ambil array data transaksi
|
||||
|
||||
if (!newStatus || !payloadData || payloadData.length === 0) {
|
||||
console.log(
|
||||
"📭 WS: No valid payload data (newStatus.data.data) in message. Skipping state change."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[WS] Menerima update data di Step ${paymentStore.currentStep}. Total records: ${payloadData.length}`
|
||||
);
|
||||
const allRecords = payloadData;
|
||||
|
||||
if (paymentStore.currentStep === 1) {
|
||||
console.log("🔍 Step 1 (WS): Looking for IP:", localIpAddress.value);
|
||||
|
||||
const relevantData = allRecords.find(
|
||||
(item) => item.ip === localIpAddress.value && item.status === "1"
|
||||
);
|
||||
|
||||
if (relevantData) {
|
||||
console.log(
|
||||
"✅ WS Data found for IP (New Transaction). Moving to Step 2."
|
||||
);
|
||||
paymentStore.updatePayment({ data: [relevantData] });
|
||||
paymentStore.setStep(2);
|
||||
} else {
|
||||
console.log(
|
||||
`⏳ No relevant data found for IP ${localIpAddress.value} in this broadcast.`
|
||||
);
|
||||
}
|
||||
} else if (paymentStore.currentStep === 2) {
|
||||
const relevantData = allRecords.find(
|
||||
(item) =>
|
||||
item.ip === localIpAddress.value &&
|
||||
item.display_nobill === paymentStore.qrData.display_nobill
|
||||
);
|
||||
|
||||
if (relevantData) {
|
||||
console.log(
|
||||
"🔄 WS Update received for current patient in Step 2. Updating store..."
|
||||
);
|
||||
paymentStore.updatePayment({ data: [relevantData] });
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const checkAndRedirect = () => {
|
||||
const savedIp = localStorage.getItem("loketIp");
|
||||
if (!savedIp) {
|
||||
router.push("/setupip");
|
||||
} else {
|
||||
localIpAddress.value = savedIp;
|
||||
wsConnect();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
checkAndRedirect();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
wsDisconnect();
|
||||
});
|
||||
</script>
|
||||
>
|
||||
|
||||
<style scoped>
|
||||
.medical-payment-app {
|
||||
background: #ff9248;
|
||||
min-height: 100vh;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
min-height: 100vh;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.contact-fullscreen {
|
||||
max-width: none !important;
|
||||
border-radius: 0 !important;
|
||||
height: auto !important;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.payment-centered {
|
||||
background: #ff9248 !important;
|
||||
max-width: 1200px !important;
|
||||
width: fit-content !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.payment-card {
|
||||
background: #ff9248;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
||||
height: fit-content !important;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.status-card {
|
||||
background: #ff9248;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
||||
height: fit-content !important;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 900px) {
|
||||
.payment-centered {
|
||||
max-width: 500px !important;
|
||||
margin: 1rem !important;
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 800px !important;
|
||||
margin: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (min-width: 768px) {
|
||||
.payment-centered {
|
||||
max-width: 1400px !important;
|
||||
margin: 0.5rem auto !important;
|
||||
min-height: calc(100vh - 1rem);
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 700px !important;
|
||||
margin: 0.5rem auto !important;
|
||||
min-height: calc(100vh - 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-height: 600px) {
|
||||
.medical-payment-app {
|
||||
min-height: 600px;
|
||||
}
|
||||
|
||||
.contact-fullscreen,
|
||||
.payment-centered,
|
||||
.status-centered {
|
||||
min-height: 600px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+99
-58
@@ -48,8 +48,7 @@ const paymentStore = usePaymentStore();
|
||||
const router = useRouter();
|
||||
const localIpAddress = ref(null);
|
||||
|
||||
// **WEB SOCKET CONFIGURATION**
|
||||
const WS_URL_BASE = "ws://10.10.150.45:8084/api/v1/ws";
|
||||
const WS_URL_BASE = "ws://10.10.150.45:8080/api/v1/ws";
|
||||
const WS_QUERY = "user_id=QRIS&room=BANKJATIM";
|
||||
const fullWsUrl = `${WS_URL_BASE}?${WS_QUERY}`;
|
||||
|
||||
@@ -81,53 +80,58 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// script page index:
|
||||
watch(
|
||||
wsData,
|
||||
(newStatus) => {
|
||||
const payloadData = newStatus?.data?.data; // Ambil array data transaksi
|
||||
const payloadData = newStatus?.data?.data;
|
||||
const posDevices = newStatus?.data?.posdevice || [];
|
||||
|
||||
if (!newStatus || !payloadData || payloadData.length === 0) {
|
||||
console.log(
|
||||
"📭 WS: No valid payload data (newStatus.data.data) in message. Skipping state change."
|
||||
);
|
||||
if (!newStatus || !payloadData) {
|
||||
console.log("📭 WS: No valid payload data in message. Skipping state change.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ... (console.log yang ada) ...
|
||||
|
||||
console.log(
|
||||
`[WS] Menerima update data di Step ${paymentStore.currentStep}. Total records: ${payloadData.length}`
|
||||
);
|
||||
const allRecords = payloadData;
|
||||
const isTargetDevice = payloadData.ip === localIpAddress.value;
|
||||
const isPosDevice = posDevices.includes(localIpAddress.value);
|
||||
const isRelevantDevice = isTargetDevice || isPosDevice; // Kita gunakan ini
|
||||
|
||||
if (paymentStore.currentStep === 1) {
|
||||
// --- LOGIKA BARU DITAMBAHKAN DI SINI ---
|
||||
// Jika kita di Step 3 (Sukses) atau 4 (Gagal) DAN ada transaksi BARU (status "1")
|
||||
if (
|
||||
(paymentStore.currentStep === 3 || paymentStore.currentStep === 4) &&
|
||||
isRelevantDevice &&
|
||||
payloadData.status === "1"
|
||||
) {
|
||||
console.log("🔄 [WS] New transaction received on end screen. Resetting state...");
|
||||
|
||||
// 1. Reset store secara paksa (ini akan membersihkan timer dan kembali ke step 1)
|
||||
paymentStore.reset();
|
||||
|
||||
// 2. Sekarang proses data baru seolah-olah kita berada di Step 1
|
||||
paymentStore.updatePayment({ data: [payloadData] });
|
||||
paymentStore.setStep(2);
|
||||
}
|
||||
// --- AKHIR LOGIKA BARU ---
|
||||
|
||||
// Logika Anda yang sudah ada
|
||||
else if (paymentStore.currentStep === 1) {
|
||||
console.log("🔍 Step 1 (WS): Looking for IP:", localIpAddress.value);
|
||||
|
||||
const relevantData = allRecords.find(
|
||||
(item) => item.ip === localIpAddress.value && item.status === "1"
|
||||
);
|
||||
|
||||
if (relevantData) {
|
||||
console.log(
|
||||
"✅ WS Data found for IP (New Transaction). Moving to Step 2."
|
||||
);
|
||||
paymentStore.updatePayment({ data: [relevantData] });
|
||||
if (isRelevantDevice && payloadData.status === "1") {
|
||||
console.log("✅ WS Data found for IP (New Transaction). Moving to Step 2.");
|
||||
paymentStore.updatePayment({ data: [payloadData] });
|
||||
paymentStore.setStep(2);
|
||||
} else {
|
||||
console.log(
|
||||
`⏳ No relevant data found for IP ${localIpAddress.value} in this broadcast.`
|
||||
);
|
||||
console.log(`⏳ No relevant data found for IP ${localIpAddress.value} in this broadcast.`);
|
||||
}
|
||||
} else if (paymentStore.currentStep === 2) {
|
||||
const relevantData = allRecords.find(
|
||||
(item) =>
|
||||
item.ip === localIpAddress.value &&
|
||||
item.display_nobill === paymentStore.qrData.display_nobill
|
||||
);
|
||||
|
||||
if (relevantData) {
|
||||
console.log(
|
||||
"🔄 WS Update received for current patient in Step 2. Updating store..."
|
||||
);
|
||||
paymentStore.updatePayment({ data: [relevantData] });
|
||||
if (isRelevantDevice &&
|
||||
payloadData.display_nobill === paymentStore.qrData.display_nobill) {
|
||||
console.log("🔄 WS Update received for current patient in Step 2. Updating store...");
|
||||
paymentStore.updatePayment({ data: [payloadData] });
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -152,7 +156,6 @@ onUnmounted(() => {
|
||||
wsDisconnect();
|
||||
});
|
||||
</script>
|
||||
>
|
||||
|
||||
<style scoped>
|
||||
.medical-payment-app {
|
||||
@@ -173,30 +176,12 @@ onUnmounted(() => {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.payment-centered {
|
||||
background: #ff9248 !important;
|
||||
max-width: 1200px !important;
|
||||
width: fit-content !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.payment-card {
|
||||
background: #ff9248;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
||||
height: fit-content !important;
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
@@ -206,8 +191,64 @@ onUnmounted(() => {
|
||||
min-height: inherit;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 900px) {
|
||||
/* PORTRAIT MODE*/
|
||||
@media (orientation: portrait) {
|
||||
.payment-centered {
|
||||
background: #ff9248 !important;
|
||||
max-width: 600px !important;
|
||||
width: 100% !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 600px !important;
|
||||
width: 100% !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
/* LANDSCAPE MODE*/
|
||||
@media (orientation: landscape) {
|
||||
.payment-centered {
|
||||
background: #ff9248 !important;
|
||||
max-width: 1200px !important;
|
||||
width: fit-content !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 1200px !important;
|
||||
width: 100% !important;
|
||||
margin: 1rem auto !important;
|
||||
border-radius: 16px !important;
|
||||
height: fit-content !important;
|
||||
min-height: calc(100vh - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Large tablet portrait */
|
||||
@media (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) {
|
||||
.payment-centered {
|
||||
max-width: 950px !important;
|
||||
}
|
||||
|
||||
.status-centered {
|
||||
max-width: 900px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Landscape responsive */
|
||||
@media (max-width: 900px) and (orientation: landscape) {
|
||||
.payment-centered {
|
||||
max-width: 500px !important;
|
||||
margin: 1rem !important;
|
||||
@@ -244,4 +285,4 @@ onUnmounted(() => {
|
||||
min-height: 600px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user