292 lines
7.5 KiB
Vue
292 lines
7.5 KiB
Vue
<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);
|
|
const config = useRuntimeConfig()
|
|
|
|
const WS_URL_BASE = config.public.url_base;
|
|
const WS_QUERY = config.public.query;
|
|
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;
|
|
const posDevices = newStatus?.data?.posdevice || [];
|
|
|
|
if (!newStatus || !payloadData) {
|
|
console.log(
|
|
"📭 WS: No valid payload data in message. Skipping state change."
|
|
);
|
|
return;
|
|
}
|
|
|
|
const isTargetDevice = payloadData.ip === localIpAddress.value;
|
|
const isPosDevice = posDevices.includes(localIpAddress.value);
|
|
const isRelevantDevice = isTargetDevice || isPosDevice; // Kita gunakan ini
|
|
|
|
if (
|
|
(paymentStore.currentStep === 3 || paymentStore.currentStep === 4) &&
|
|
isRelevantDevice &&
|
|
payloadData.status === "1"
|
|
) {
|
|
console.log(
|
|
"🔄 [WS] New transaction received on end screen. Resetting state..."
|
|
);
|
|
|
|
paymentStore.reset();
|
|
|
|
paymentStore.updatePayment({ data: [payloadData] });
|
|
paymentStore.setStep(2);
|
|
} else if (paymentStore.currentStep === 1) {
|
|
console.log("🔍 Step 1 (WS): Looking for IP:", localIpAddress.value);
|
|
|
|
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.`
|
|
);
|
|
}
|
|
} else if (paymentStore.currentStep === 2) {
|
|
if (isRelevantDevice) {
|
|
console.log(
|
|
"🔄 WS Update received for current patient in Step 2. Updating store..."
|
|
);
|
|
paymentStore.updatePayment({ data: [payloadData] });
|
|
}
|
|
// 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] });
|
|
// }
|
|
}
|
|
},
|
|
{ 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-card {
|
|
background: #ff9248;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
height: fit-content !important;
|
|
min-height: inherit;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.status-card {
|
|
background: #ff9248;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
height: fit-content !important;
|
|
min-height: inherit;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
.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>
|