Files
web-antrean/components/checkin/GenerateQRTab.vue
T
2026-01-05 08:32:59 +07:00

326 lines
8.9 KiB
Vue

<script setup lang="ts">
import QRCode from 'qrcode';
import { onMounted } from 'vue';
const props = defineProps<{
patientId: string;
status: string;
generatedQRData: string | null;
statusOptions: Array<{ title: string; value: string }>;
primaryColor: string;
generateRandomPatientId: () => string;
}>();
const emit = defineEmits<{
'update:patientId': [value: string];
'update:status': [value: string];
'generate': [];
'quick-generate': [patientId: string, status: string];
'download': [];
'copy': [];
'share': [];
'generate-random-id': [];
}>();
const localPatientId = computed({
get: () => props.patientId,
set: (value) => emit('update:patientId', value),
});
const localStatus = computed({
get: () => props.status,
set: (value) => emit('update:status', value),
});
const handleGenerate = () => {
emit('generate');
};
const handleQuickGenerate = (status: string) => {
const randomId = props.generateRandomPatientId();
emit('quick-generate', randomId, status);
};
const handleDownload = () => {
emit('download');
};
const handleCopy = () => {
emit('copy');
};
const handleShare = () => {
emit('share');
};
const handleGenerateRandomId = () => {
emit('generate-random-id');
};
const qrContainerRef = ref<HTMLDivElement | null>(null);
// Function to render QR code
const renderQRCode = async (data: string) => {
if (!data) {
return;
}
// Wait for DOM to be ready
await nextTick();
// Wait a bit more to ensure ref is mounted (especially when switching tabs)
await new Promise(resolve => setTimeout(resolve, 150));
if (!qrContainerRef.value) {
console.warn('QR container ref not available yet, retrying...');
// Retry after a short delay
setTimeout(() => {
if (qrContainerRef.value && data) {
renderQRCode(data);
}
}, 300);
return;
}
try {
// Clear previous content
qrContainerRef.value.innerHTML = '';
// Generate QR code
const qrDataUrl = await QRCode.toDataURL(data, {
errorCorrectionLevel: 'M',
type: 'image/png',
quality: 0.92,
margin: 1,
color: {
dark: '#000000',
light: '#FFFFFF',
},
width: 300,
});
// Create and append image
const img = document.createElement('img');
img.src = qrDataUrl;
img.alt = 'QR Code';
img.style.width = '100%';
img.style.maxWidth = '300px';
img.style.height = 'auto';
img.style.display = 'block';
img.style.margin = '0 auto';
qrContainerRef.value.appendChild(img);
} catch (error) {
console.error('Error generating QR code:', error);
}
};
// Watch for generatedQRData changes to render QR code
watch(() => props.generatedQRData, async (newData) => {
if (newData) {
// Use setTimeout to ensure DOM is ready, especially when switching tabs
setTimeout(() => {
renderQRCode(newData);
}, 200);
} else if (qrContainerRef.value) {
// Clear container if data is cleared
qrContainerRef.value.innerHTML = '';
}
}, { immediate: true });
// Also watch for when component is mounted and ref is ready
onMounted(() => {
if (props.generatedQRData) {
// Delay to ensure ref is mounted
setTimeout(() => {
renderQRCode(props.generatedQRData!);
}, 300);
}
});
</script>
<template>
<div class="tab-content">
<!-- Status Header -->
<div class="status-header mb-3">
<div class="status-icon-wrapper">
<v-icon :color="primaryColor" size="24">mdi-qrcode</v-icon>
</div>
<div class="status-text">
<h3 class="status-title">Generate QR Code untuk Testing</h3>
<p class="status-subtitle">Buat QR code yang bisa Anda scan di tab "Scan QR"</p>
</div>
</div>
<!-- Quick Preset Buttons -->
<div class="mb-3">
<p class="text-caption text-grey text-center mb-2">Quick Test QR Codes:</p>
<v-row dense>
<v-col cols="6">
<v-btn
variant="outlined"
color="success"
size="small"
@click="handleQuickGenerate('ALLOWED')"
class="text-none"
block
>
<v-icon start size="16">mdi-check-circle</v-icon>
Test ALLOWED
</v-btn>
</v-col>
<v-col cols="6">
<v-btn
variant="outlined"
color="warning"
size="small"
@click="handleQuickGenerate('NOT_ALLOWED')"
class="text-none"
block
>
<v-icon start size="16">mdi-clock-alert</v-icon>
Test NOT_ALLOWED
</v-btn>
</v-col>
</v-row>
</div>
<!-- Form Generate -->
<v-form @submit.prevent="handleGenerate">
<v-text-field
v-model="localPatientId"
label="ID Pasien"
placeholder="Contoh: P-123456"
prepend-inner-icon="mdi-identifier"
variant="outlined"
:color="primaryColor"
class="input-modern mb-3"
density="comfortable"
clearable
hide-details="auto"
>
<template #append-inner>
<v-btn
icon
size="small"
variant="text"
@click="handleGenerateRandomId"
title="Generate Random ID"
>
<v-icon size="20">mdi-refresh</v-icon>
</v-btn>
</template>
</v-text-field>
<v-select
v-model="localStatus"
label="Status Check-in"
:items="statusOptions"
prepend-inner-icon="mdi-shield-check"
variant="outlined"
:color="primaryColor"
class="input-modern mb-4"
density="comfortable"
hide-details="auto"
></v-select>
<div class="d-flex justify-center">
<v-btn
class="btn-primary-modern btn-centered"
size="large"
type="submit"
elevation="0"
:disabled="!localPatientId"
>
<v-icon start size="20">mdi-qrcode-plus</v-icon>
Generate QR Code
</v-btn>
</div>
</v-form>
<!-- QR Code Display -->
<div v-if="props.generatedQRData" class="qr-display mt-6">
<v-card variant="outlined" class="pa-4">
<div class="text-center">
<p class="text-subtitle-2 text-grey mb-3">QR Code Anda:</p>
<div ref="qrContainerRef" class="qr-code-container mb-4"></div>
<v-chip :color="localStatus === 'ALLOWED' ? 'success' : 'warning'" class="mb-3">
<v-icon start>{{ localStatus === 'ALLOWED' ? 'mdi-check' : 'mdi-clock-alert' }}</v-icon>
{{ localStatus === 'ALLOWED' ? 'Diizinkan Check-in' : 'Belum Diizinkan' }}
</v-chip>
<p class="text-body-2 text-grey mb-4">
Data: {{ props.generatedQRData }}
</p>
<!-- Action Buttons -->
<v-row dense>
<v-col cols="4">
<v-btn
variant="outlined"
:color="primaryColor"
block
size="small"
@click="handleDownload"
class="text-none"
>
<v-icon start size="18">mdi-download</v-icon>
Download
</v-btn>
</v-col>
<v-col cols="4">
<v-btn
variant="outlined"
:color="primaryColor"
block
size="small"
@click="handleCopy"
class="text-none"
>
<v-icon start size="18">mdi-content-copy</v-icon>
Copy
</v-btn>
</v-col>
<v-col cols="4">
<v-btn
variant="outlined"
:color="primaryColor"
block
size="small"
@click="handleShare"
class="text-none"
>
<v-icon start size="18">mdi-share-variant</v-icon>
Share
</v-btn>
</v-col>
</v-row>
</div>
</v-card>
<!-- Instructions -->
<v-alert
type="success"
variant="tonal"
class="mt-4 text-body-2"
>
<template #prepend>
<v-icon>mdi-information</v-icon>
</template>
<strong>Cara menggunakan untuk Testing:</strong>
<ol class="ml-4 mt-2">
<li>Gunakan tombol <strong>"Test ALLOWED"</strong> atau <strong>"Test NOT_ALLOWED"</strong> untuk generate QR cepat, atau isi form manual</li>
<li>Klik <strong>"Download"</strong> untuk menyimpan QR code ke komputer</li>
<li>Buka file QR code yang didownload (bisa di HP atau layar lain)</li>
<li>Pindah ke tab <strong>"Scan QR"</strong> dan scan QR code tersebut</li>
<li>Atau gunakan <strong>"Copy"</strong> untuk menyalin QR ke clipboard dan paste di aplikasi lain</li>
</ol>
</v-alert>
</div>
</div>
</template>
<style scoped lang="scss">
@import '~/assets/scss/checkin/components';
</style>