StatusBadge: surface leave/holiday suffix as secondary chip

status_text fields like '異常-遲到48分 + SL2h' or '缺勤 (🎉香港特區…)' carried
important leave/holiday context that the simplified badge was dropping on the
list page — the user had no way to tell an abnormal record was partly covered
by sick leave.

Extract the suffix from status_text and render a small secondary chip next
to the basic-status badge (violet for leave types, indigo for holidays).
Detail page picks this up automatically since the component is shared.

Example: '異常 +SL2h' / '缺勤 🎉香港特區成立紀念日'
This commit is contained in:
IT狗
2026-07-21 22:31:26 +08:00
parent 3ba6617009
commit fcc3246d8e
+52 -7
View File
@@ -11,9 +11,13 @@ import { CheckCircle, AlertCircle, CalendarX, Palmtree, Briefcase } from 'lucide
* - normal → 正常 (green) * - normal → 正常 (green)
* - everything else (late/early/ot/abnormal/late_early/...) → 異常 (orange) * - everything else (late/early/ot/abnormal/late_early/...) → 異常 (orange)
* *
* If status_text carries a leave/holiday suffix (e.g. '+ SL2h', '🎉香港…'),
* a small secondary chip is rendered next to the main badge so the list page
* shows the underlying leave/holiday detail without expanding the row.
*
* Props: * Props:
* - code: status_code string * - code: status_code string
* - text: status_text string (fallback signal) * - text: status_text string (fallback signal + suffix source)
* - size: 'sm' | 'md' (default 'sm') * - size: 'sm' | 'md' (default 'sm')
*/ */
export default function StatusBadge({ code = '', text = '', size = 'sm' }) { export default function StatusBadge({ code = '', text = '', size = 'sm' }) {
@@ -28,32 +32,73 @@ export default function StatusBadge({ code = '', text = '', size = 'sm' }) {
</span> </span>
) )
const c = String(code || '').toLowerCase() // Extract leave/holiday suffix from status_text:
// '異常-遲到48分 + SL2h' → ['SL2h']
// '缺勤 (🎉香港特別行政區成立紀念日)' → ['🎉香港特別行政區成立紀念日']
// 'OT3分 + AL4h + SL3h' → ['AL4h', 'SL3h']
// '正常 + CL4h' → ['CL4h']
const t = String(text || '') const t = String(text || '')
const noteParts = []
const split = t.split(' + ').map(s => s.trim())
if (split.length > 1) {
for (let i = 1; i < split.length; i++) {
noteParts.push(split[i].replace(/^\(|\)$/g, '').trim())
}
} else if (t.includes('🎉')) {
const m = t.match(/🎉[^)\s]+(?:\s[^)\s]+)*/)
if (m) noteParts.push(m[0])
}
const noteChips = noteParts.filter(Boolean).map((part, idx) => {
// Strip trailing ')'
const clean = part.replace(/\)+$/, '')
const isHoliday = clean.startsWith('🎉')
const cls = isHoliday
? 'bg-indigo-50 text-indigo-600 border border-indigo-200'
: 'bg-violet-50 text-violet-600 border border-violet-200'
return (
<span
key={idx}
className={`inline-flex items-center ${sizeCls} rounded-full font-normal ${cls}`}
title={clean}
>
{isHoliday ? clean : `+${clean}`}
</span>
)
})
const wrap = (content) => (
<span className="inline-flex items-center gap-1 flex-wrap">
{content}
{noteChips}
</span>
)
const c = String(code || '').toLowerCase()
// 缺勤 (missing) // 缺勤 (missing)
if (c === 'missing' || t === '缺勤') { if (c === 'missing' || t === '缺勤') {
return badge(<CalendarX size={12} />, '缺勤', 'bg-gray-200 text-gray-700') return wrap(badge(<CalendarX size={12} />, '缺勤', 'bg-gray-200 text-gray-700'))
} }
// 公假 (holiday) // 公假 (holiday)
if (c === 'holiday' || t.includes('公假') || t.includes('🏖️')) { if (c === 'holiday' || t.includes('公假') || t.includes('🏖️')) {
return badge(<Palmtree size={12} />, '公假', 'bg-indigo-100 text-indigo-700') return wrap(badge(<Palmtree size={12} />, '公假', 'bg-indigo-100 text-indigo-700'))
} }
// 請假 (any leave type) // 請假 (any leave type)
if (['al', 'sl', 'cl', 'mixed_leave'].includes(c) if (['al', 'sl', 'cl', 'mixed_leave'].includes(c)
|| t.includes('年假') || t.includes('病假') || t.includes('補鐘') || t.includes('年假') || t.includes('病假') || t.includes('補鐘')
|| t.includes('混合假')) { || t.includes('混合假')) {
return badge(<Briefcase size={12} />, '請假', 'bg-violet-100 text-violet-700') return wrap(badge(<Briefcase size={12} />, '請假', 'bg-violet-100 text-violet-700'))
} }
// 正常 (only when status_code is 'normal' — text suffix like '+ CL4h' is allowed, // 正常 (only when status_code is 'normal' — text suffix like '+ CL4h' is allowed,
// we still want to flag it as 正常 first since the employee did clock in/out normally) // we still want to flag it as 正常 first since the employee did clock in/out normally)
if (c === 'normal') { if (c === 'normal') {
return badge(<CheckCircle size={12} />, '正常', 'bg-green-100 text-green-700') return wrap(badge(<CheckCircle size={12} />, '正常', 'bg-green-100 text-green-700'))
} }
// 異常 (late/early/ot/abnormal/late_early/late_ot/early_ot/...) // 異常 (late/early/ot/abnormal/late_early/late_ot/early_ot/...)
return badge(<AlertCircle size={12} />, '異常', 'bg-orange-100 text-orange-700') return wrap(badge(<AlertCircle size={12} />, '異常', 'bg-orange-100 text-orange-700'))
} }