212 lines
7.1 KiB
TypeScript
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,
|
|
}
|
|
}
|