QRscan implementation
This commit is contained in:
No files matched your search
@@ -0,0 +1,276 @@
|
||||
import { ref, computed, readonly } from 'vue'
|
||||
import type { CheckInHistoryItem, ScannedQRHistoryItem, HistoryStatus } from '~/types/checkin'
|
||||
import { HISTORY_STORAGE_KEY, SCANNED_QR_STORAGE_KEY, MAX_HISTORY_ITEMS, MAX_SCANNED_QR_ITEMS, HISTORY_STATUS_OPTIONS } from '~/constants/checkin'
|
||||
|
||||
export const useCheckInHistory = () => {
|
||||
const checkInHistory = ref<CheckInHistoryItem[]>([])
|
||||
const scannedQRHistory = ref<ScannedQRHistoryItem[]>([])
|
||||
const historySearch = ref('')
|
||||
const historyStatusFilter = ref<HistoryStatus | ''>('')
|
||||
const historyDateFilter = ref<string>('')
|
||||
|
||||
const loadHistory = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const stored = localStorage.getItem(HISTORY_STORAGE_KEY)
|
||||
if (stored) {
|
||||
try {
|
||||
checkInHistory.value = JSON.parse(stored)
|
||||
} catch (e) {
|
||||
console.error('Error loading history:', e)
|
||||
checkInHistory.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveToHistory = (item: CheckInHistoryItem) => {
|
||||
const historyItem = {
|
||||
...item,
|
||||
queueNumber: item.queueNumber || `ANT-${Date.now()}`,
|
||||
}
|
||||
|
||||
checkInHistory.value.unshift(historyItem)
|
||||
|
||||
// Simpan maksimal item sesuai constant
|
||||
if (checkInHistory.value.length > MAX_HISTORY_ITEMS) {
|
||||
checkInHistory.value = checkInHistory.value.slice(0, MAX_HISTORY_ITEMS)
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(checkInHistory.value))
|
||||
}
|
||||
}
|
||||
|
||||
const deleteHistoryItem = (index: number) => {
|
||||
checkInHistory.value.splice(index, 1)
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(HISTORY_STORAGE_KEY, JSON.stringify(checkInHistory.value))
|
||||
}
|
||||
}
|
||||
|
||||
const clearHistory = () => {
|
||||
checkInHistory.value = []
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem(HISTORY_STORAGE_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
const loadScannedQRHistory = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const stored = localStorage.getItem(SCANNED_QR_STORAGE_KEY)
|
||||
if (stored) {
|
||||
try {
|
||||
scannedQRHistory.value = JSON.parse(stored)
|
||||
} catch (e) {
|
||||
console.error('Error loading QR history:', e)
|
||||
scannedQRHistory.value = []
|
||||
}
|
||||
} else {
|
||||
scannedQRHistory.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveScannedQRData = (qrData: string) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const scannedQRs = JSON.parse(localStorage.getItem(SCANNED_QR_STORAGE_KEY) || '[]')
|
||||
scannedQRs.unshift({
|
||||
data: qrData,
|
||||
timestamp: new Date().toISOString(),
|
||||
date: new Date().toLocaleDateString('id-ID'),
|
||||
time: new Date().toLocaleTimeString('id-ID')
|
||||
})
|
||||
|
||||
// Simpan maksimal item sesuai constant
|
||||
if (scannedQRs.length > MAX_SCANNED_QR_ITEMS) {
|
||||
scannedQRs.pop()
|
||||
}
|
||||
|
||||
localStorage.setItem(SCANNED_QR_STORAGE_KEY, JSON.stringify(scannedQRs))
|
||||
}
|
||||
}
|
||||
|
||||
const clearQRHistory = () => {
|
||||
scannedQRHistory.value = []
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.removeItem(SCANNED_QR_STORAGE_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteQRHistoryItem = (index: number) => {
|
||||
scannedQRHistory.value.splice(index, 1)
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(SCANNED_QR_STORAGE_KEY, JSON.stringify(scannedQRHistory.value))
|
||||
}
|
||||
}
|
||||
|
||||
const filteredHistory = computed(() => {
|
||||
let filtered = [...checkInHistory.value]
|
||||
|
||||
// Filter by search
|
||||
if (historySearch.value) {
|
||||
const search = historySearch.value.toLowerCase()
|
||||
filtered = filtered.filter(item =>
|
||||
item.patientId.toLowerCase().includes(search) ||
|
||||
(item.queueNumber && item.queueNumber.toLowerCase().includes(search))
|
||||
)
|
||||
}
|
||||
|
||||
// Filter by status
|
||||
if (historyStatusFilter.value) {
|
||||
filtered = filtered.filter(item => {
|
||||
if (historyStatusFilter.value === 'success') {
|
||||
return item.status === 'ALLOWED' || item.status === 'success'
|
||||
} else if (historyStatusFilter.value === 'failed') {
|
||||
return item.status === 'NOT_ALLOWED' || item.status === 'failed'
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Filter by date
|
||||
if (historyDateFilter.value && historyDateFilter.value.trim() !== '') {
|
||||
filtered = filtered.filter(item => {
|
||||
if (!item.checkInDate) return false
|
||||
try {
|
||||
const itemDate = new Date(item.checkInDate)
|
||||
const filterDate = new Date(historyDateFilter.value)
|
||||
|
||||
// Validate dates
|
||||
if (isNaN(itemDate.getTime()) || isNaN(filterDate.getTime())) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare dates (year, month, day only, ignore time)
|
||||
const itemDateStr = itemDate.toISOString().substring(0, 10)
|
||||
const filterDateStr = filterDate.toISOString().substring(0, 10)
|
||||
|
||||
return itemDateStr === filterDateStr
|
||||
} catch (e) {
|
||||
console.error('Error filtering by date:', e)
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
const filteredQRHistory = computed(() => {
|
||||
let filtered = [...scannedQRHistory.value]
|
||||
|
||||
// Filter by search
|
||||
if (historySearch.value) {
|
||||
const search = historySearch.value.toLowerCase()
|
||||
filtered = filtered.filter(item =>
|
||||
item.data.toLowerCase().includes(search)
|
||||
)
|
||||
}
|
||||
|
||||
// Filter by date
|
||||
if (historyDateFilter.value && historyDateFilter.value.trim() !== '') {
|
||||
filtered = filtered.filter(item => {
|
||||
if (!item.timestamp && !item.date) return false
|
||||
try {
|
||||
let itemDate: Date
|
||||
if (item.timestamp) {
|
||||
itemDate = new Date(item.timestamp)
|
||||
} else {
|
||||
// Parse Indonesian date format (dd/mm/yyyy or dd mmm yyyy)
|
||||
itemDate = new Date(item.date)
|
||||
}
|
||||
const filterDate = new Date(historyDateFilter.value)
|
||||
|
||||
// Validate dates
|
||||
if (isNaN(itemDate.getTime()) || isNaN(filterDate.getTime())) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare dates (year, month, day only, ignore time)
|
||||
const itemDateStr = itemDate.toISOString().substring(0, 10)
|
||||
const filterDateStr = filterDate.toISOString().substring(0, 10)
|
||||
|
||||
return itemDateStr === filterDateStr
|
||||
} catch (e) {
|
||||
console.error('Error filtering QR history by date:', e)
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
if (status === 'ALLOWED' || status === 'success') return 'success'
|
||||
if (status === 'NOT_ALLOWED' || status === 'failed') return 'error'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
if (status === 'ALLOWED' || status === 'success') return 'mdi-check-circle'
|
||||
if (status === 'NOT_ALLOWED' || status === 'failed') return 'mdi-close-circle'
|
||||
return 'mdi-clock-alert'
|
||||
}
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
if (status === 'ALLOWED' || status === 'success') return 'Berhasil'
|
||||
if (status === 'NOT_ALLOWED' || status === 'failed') return 'Gagal'
|
||||
return 'Pending'
|
||||
}
|
||||
|
||||
const getStatusClass = (status: string) => {
|
||||
if (status === 'ALLOWED' || status === 'success') return 'history-success'
|
||||
if (status === 'NOT_ALLOWED' || status === 'failed') return 'history-failed'
|
||||
return 'history-pending'
|
||||
}
|
||||
|
||||
const formatDateTime = (dateString: string) => {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('id-ID', {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
// Load history on initialization
|
||||
if (typeof window !== 'undefined') {
|
||||
loadHistory()
|
||||
loadScannedQRHistory()
|
||||
}
|
||||
|
||||
return {
|
||||
checkInHistory: readonly(checkInHistory),
|
||||
scannedQRHistory: readonly(scannedQRHistory),
|
||||
historySearch,
|
||||
historyStatusFilter,
|
||||
historyDateFilter,
|
||||
filteredHistory,
|
||||
filteredQRHistory,
|
||||
loadHistory,
|
||||
saveToHistory,
|
||||
deleteHistoryItem,
|
||||
clearHistory,
|
||||
loadScannedQRHistory,
|
||||
saveScannedQRData,
|
||||
clearQRHistory,
|
||||
deleteQRHistoryItem,
|
||||
getStatusColor,
|
||||
getStatusIcon,
|
||||
getStatusText,
|
||||
getStatusClass,
|
||||
formatDateTime,
|
||||
formatDate,
|
||||
historyStatusOptions: [...HISTORY_STATUS_OPTIONS] as Array<{ title: string; value: string }>,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user