Files
web-antrean/composables/useCheckInHistory.ts
2026-01-14 08:25:47 +07:00

279 lines
8.5 KiB
TypeScript

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').replace(/\./g, ':')
})
// 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)
// Format jam dengan titik dua (HH:MM:SS)
const timeString = date.toLocaleTimeString('id-ID', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
return timeString.replace(/\./g, ':')
}
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 }>,
}
}