Files
web-antrean/composables/useQRGenerator.ts
T
2026-01-05 08:32:59 +07:00

212 lines
7.1 KiB
TypeScript

import { ref, readonly, computed } from 'vue'
import { nextTick } from 'vue'
import type { CheckInStatus } from '~/types/checkin'
export interface UseQRGeneratorOptions {
showSnackbar: (title: string, message: string, color: string, icon: string) => void
generateRandomPatientId: () => string
}
export const useQRGenerator = (options: UseQRGeneratorOptions) => {
const { showSnackbar, generateRandomPatientId } = options
// State
const generatePatientId = ref(generateRandomPatientId())
const generateStatus = ref<CheckInStatus>('ALLOWED')
const generatedQRData = ref('')
// Generate random patient ID
const generateRandomId = () => {
generatePatientId.value = generateRandomPatientId()
}
// Quick generate QR for testing
const generateQuickQR = (patientId: string, status: string) => {
generatePatientId.value = patientId
generateStatus.value = status as CheckInStatus
generateQRCode()
}
// Generate QR Code function
const generateQRCode = async () => {
if (!generatePatientId.value) {
showSnackbar('Error', 'Mohon isi ID Pasien', 'error', 'mdi-alert')
return
}
generatedQRData.value = `${generatePatientId.value}|${generateStatus.value}`
await nextTick()
// Clear previous QR code
const qrContainer = document.getElementById('qrcode')
if (qrContainer) {
qrContainer.innerHTML = ''
try {
// Use qrcode package that's already installed
const QRCode = (await import('qrcode')).default
// Create QR code as data URL
const qrDataUrl = await QRCode.toDataURL(generatedQRData.value, {
errorCorrectionLevel: 'M',
type: 'image/png',
quality: 0.92,
margin: 1,
color: {
dark: '#000000',
light: '#FFFFFF'
},
width: 300
})
// Create img element and append to container
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'
qrContainer.appendChild(img)
console.log('QR Code created successfully:', generatedQRData.value)
showSnackbar('Berhasil!', 'QR Code berhasil di-generate. Silakan scan untuk testing', 'success', 'mdi-check-circle')
} catch (error) {
console.error('Error creating QR code:', error)
showSnackbar('Error', 'Gagal membuat QR Code. Silakan coba lagi', 'error', 'mdi-alert')
}
} else {
showSnackbar('Error', 'Container QR Code tidak ditemukan', 'error', 'mdi-alert')
}
}
// Download QR Code
const downloadQR = async () => {
const img = document.querySelector('#qrcode img') as HTMLImageElement
if (img && img.src) {
try {
// Convert img src (data URL) to blob
const response = await fetch(img.src)
const blob = await response.blob()
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
const fileName = `QR-Test-${generatePatientId.value}-${generateStatus.value}-${Date.now()}.png`
link.download = fileName
link.href = url
link.style.display = 'none'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
showSnackbar('Berhasil!', `QR Code berhasil didownload: ${fileName}`, 'success', 'mdi-download')
} catch (error) {
console.error('Download error:', error)
// Fallback: use img src directly
const link = document.createElement('a')
link.download = `QR-Test-${generatePatientId.value}-${generateStatus.value}.png`
link.href = img.src
link.click()
showSnackbar('Berhasil!', 'QR Code berhasil didownload', 'success', 'mdi-download')
}
} else {
showSnackbar('Error', 'QR Code belum di-generate. Silakan generate terlebih dahulu', 'error', 'mdi-alert')
}
}
// Copy QR Code to Clipboard
const copyQRToClipboard = async () => {
const img = document.querySelector('#qrcode img') as HTMLImageElement
if (img && img.src) {
try {
// Convert img src to blob
const response = await fetch(img.src)
const blob = await response.blob()
try {
await navigator.clipboard.write([
new ClipboardItem({
'image/png': blob
})
])
showSnackbar('Berhasil!', 'QR Code berhasil disalin ke clipboard', 'success', 'mdi-content-copy')
} catch (err: any) {
console.error('Clipboard error:', err)
// Fallback: download instead
showSnackbar('Info', 'Copy ke clipboard tidak didukung. Gunakan tombol Download.', 'info', 'mdi-information')
}
} catch (error) {
console.error('Copy error:', error)
showSnackbar('Error', 'Gagal menyalin QR Code', 'error', 'mdi-alert')
}
} else {
showSnackbar('Error', 'QR Code belum di-generate. Silakan generate terlebih dahulu', 'error', 'mdi-alert')
}
}
// Share QR Code
const shareQR = async () => {
const img = document.querySelector('#qrcode img') as HTMLImageElement
if (img && img.src) {
try {
// Convert img src to blob
const response = await fetch(img.src)
const blob = await response.blob()
const file = new File([blob], `QR-Test-${generatePatientId.value}-${generateStatus.value}.png`, { type: 'image/png' })
if (navigator.share && navigator.canShare({ files: [file] })) {
try {
await navigator.share({
files: [file],
title: 'QR Code Check-in Test',
text: `QR Code untuk testing: ${generatedQRData.value}`
})
showSnackbar('Berhasil!', 'QR Code berhasil dibagikan', 'success', 'mdi-share')
} catch (err: any) {
if (err.name !== 'AbortError') {
// Fallback to copy or download
copyQRToClipboard()
}
}
} else {
// Fallback: try copy to clipboard
copyQRToClipboard()
}
} catch (error) {
console.error('Share error:', error)
showSnackbar('Error', 'Gagal membagikan QR Code', 'error', 'mdi-alert')
}
} else {
showSnackbar('Error', 'QR Code belum di-generate. Silakan generate terlebih dahulu', 'error', 'mdi-alert')
}
}
// Computed untuk v-model
const generatePatientIdModel = computed({
get: () => generatePatientId.value,
set: (value: string) => {
generatePatientId.value = value
}
})
const generateStatusModel = computed({
get: () => generateStatus.value,
set: (value: CheckInStatus) => {
generateStatus.value = value
}
})
return {
generatePatientId: generatePatientIdModel,
generateStatus: generateStatusModel,
generatedQRData: readonly(generatedQRData),
generateRandomId,
generateQuickQR,
generateQRCode,
downloadQR,
copyQRToClipboard,
shareQR,
}
}