279 lines
8.5 KiB
TypeScript
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 }>,
|
|
}
|
|
}
|