guardia-messenger/components/SlaTimer.tsx

66 lines
1.9 KiB
TypeScript

import { useEffect, useRef, useState } from 'react'
import { Text, StyleSheet, Vibration } from 'react-native'
interface Props {
deadline: string // ISO 문자열
onExpire?: () => void
}
const URGENT_MS = 30 * 60 * 1000 // 30분
/**
* 기능 #3 — SLA 카운트다운 타이머
* 매 초 갱신, 임박(30분 이내) 시 빨간색 + 1회 진동
*/
export default function SlaTimer({ deadline, onExpire }: Props) {
const [remaining, setRemaining] = useState<number>(
() => new Date(deadline).getTime() - Date.now()
)
const buzzedRef = useRef(false)
const expiredRef = useRef(false)
useEffect(() => {
buzzedRef.current = false
expiredRef.current = false
const tick = () => {
const r = new Date(deadline).getTime() - Date.now()
setRemaining(r)
if (r < URGENT_MS && r > 0 && !buzzedRef.current) {
buzzedRef.current = true
try { Vibration.vibrate([0, 500, 200, 500]) } catch {}
}
if (r <= 0 && !expiredRef.current) {
expiredRef.current = true
onExpire?.()
}
}
tick()
const interval = setInterval(tick, 1000)
return () => clearInterval(interval)
}, [deadline, onExpire])
const overdue = remaining < 0
const abs = Math.abs(remaining)
const hours = Math.floor(abs / 3600000)
const minutes = Math.floor((abs % 3600000) / 60000)
const seconds = Math.floor((abs % 60000) / 1000)
const isUrgent = remaining < URGENT_MS
const pad = (n: number) => String(n).padStart(2, '0')
const label = overdue
? `초과 ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
: `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
return (
<Text style={[styles.timer, isUrgent && styles.urgent, overdue && styles.overdue]}>
{label}
</Text>
)
}
const styles = StyleSheet.create({
timer: { fontVariant: ['tabular-nums'], fontWeight: '700', fontSize: 15, color: '#64748B' },
urgent: { color: '#f59e0b' },
overdue: { color: '#ef4444' },
})