File size: 6,116 Bytes
cd99321
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { useEffect, useState } from 'react'
import { Briefcase, Pencil } from 'lucide-react'
import { useVacayStore } from '../../store/vacayStore'
import { useAuthStore } from '../../store/authStore'
import { useTranslation } from '../../i18n'
import type { VacayStat, TranslationFn } from '../../types'


export default function VacayStats() {
  const { t } = useTranslation()
  const { stats, selectedYear, loadStats, updateVacationDays, isFused } = useVacayStore()
  const { user: currentUser } = useAuthStore()

  useEffect(() => { loadStats(selectedYear) }, [selectedYear])

  return (
    <div className="rounded-xl border p-3 bg-surface-card border-edge">
      <div className="flex items-center gap-1.5 mb-3">
        <Briefcase size={13} className="text-content-faint" />
        <span className="text-[11px] font-medium uppercase tracking-wider text-content-faint">
          {t('vacay.entitlement')} {selectedYear}
        </span>
      </div>

      {stats.length === 0 ? (
        <p className="text-[11px] text-center py-3 text-content-faint">{t('vacay.noData')}</p>
      ) : (
        <div className="space-y-2">
          {stats.map(s => (
            <StatCard
              key={s.user_id}
              stat={s}
              isMe={s.user_id === currentUser?.id}
              canEdit={s.user_id === currentUser?.id || isFused}
              selectedYear={selectedYear}
              onSave={updateVacationDays}
              t={t}
            />
          ))}
        </div>
      )}
    </div>
  )
}

interface StatCardProps {
  stat: VacayStat
  isMe: boolean
  canEdit: boolean
  selectedYear: number
  onSave: (userId: number, year: number, days: number) => Promise<void>
  t: TranslationFn
}

function StatCard({ stat: s, isMe, canEdit, selectedYear, onSave, t }: StatCardProps) {
  const [editing, setEditing] = useState(false)
  // Holds the entitlement-day value while editing: a number on load, a string
  // once the user types into the number input.
  const [localDays, setLocalDays] = useState<number | string>(s.vacation_days)
  const pct = s.total_available > 0 ? Math.min(100, (s.used / s.total_available) * 100) : 0

  // Sync local state when stats reload from server
  useEffect(() => {
    if (!editing) setLocalDays(s.vacation_days)
  }, [s.vacation_days, editing])

  const handleSave = () => {
    setEditing(false)
    const days = parseInt(String(localDays))
    if (!isNaN(days) && days >= 0 && days <= 365 && days !== s.vacation_days) {
      onSave(selectedYear, days, s.user_id)
    }
  }

  return (
    <div className="rounded-lg p-2.5 space-y-2 border border-edge-secondary">
      <div className="flex items-center gap-2">
        <span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: s.person_color }} />
        <span className="text-xs font-semibold flex-1 truncate text-content">
          {s.person_name}
          {isMe && <span className="text-content-faint"> ({t('vacay.you')})</span>}
        </span>
        <span className="text-[10px] tabular-nums text-content-faint">{s.used}/{s.total_available}</span>
      </div>
      <div className="h-1.5 rounded-full overflow-hidden bg-surface-secondary">
        <div
          className="trek-bar-fill h-full rounded-full transition-[width] duration-500 ease-[cubic-bezier(0.23,1,0.32,1)]"
          style={{ width: `${pct}%`, backgroundColor: s.person_color }}
        />
      </div>
      <div className="grid grid-cols-3 gap-1.5">
        {/* Days — editable */}
        <div
          className={`rounded-md px-2 py-2 group/days border ${canEdit ? 'bg-surface-card border-edge' : 'bg-surface-secondary border-transparent'}`}
          style={{
            cursor: canEdit ? 'pointer' : 'default',
          }}
          onClick={() => { if (canEdit && !editing) setEditing(true) }}
        >
          <div className="text-[10px] mb-1 text-content-faint" style={{ height: 14, lineHeight: '14px' }}>
            {t('vacay.entitlementDays')} {canEdit && !editing && <Pencil size={9} className="inline opacity-0 group-hover/days:opacity-100 transition-opacity text-content-faint" style={{ verticalAlign: 'middle' }} />}
          </div>
          {editing ? (
            <input
              type="number"
              value={localDays}
              onChange={e => setLocalDays(e.target.value)}
              onBlur={handleSave}
              onKeyDown={e => { if (e.key === 'Enter') handleSave(); if (e.key === 'Escape') { setEditing(false); setLocalDays(s.vacation_days) } }}
              autoFocus
              className="w-full bg-transparent text-sm font-bold outline-none p-0 m-0 [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none text-content"
              style={{ height: 18, lineHeight: '18px' }}
            />
          ) : (
            <div className="text-sm font-bold text-content" style={{ height: 18, lineHeight: '18px' }}>{s.vacation_days}</div>
          )}
        </div>
        {/* Used */}
        <div className="rounded-md px-2 py-2 bg-surface-secondary">
          <div className="text-[10px] mb-1 text-content-faint" style={{ height: 14, lineHeight: '14px' }}>{t('vacay.used')}</div>
          <div className="text-sm font-bold text-content" style={{ height: 18, lineHeight: '18px' }}>{s.used}</div>
        </div>
        {/* Remaining */}
        <div className="rounded-md px-2 py-2 bg-surface-secondary">
          <div className="text-[10px] mb-1 text-content-faint" style={{ height: 14, lineHeight: '14px' }}>{t('vacay.remaining')}</div>
          <div className="text-sm font-bold" style={{ color: s.remaining < 0 ? '#ef4444' : s.remaining <= 3 ? '#f59e0b' : '#22c55e', height: 18, lineHeight: '18px' }}>
            {s.remaining}
          </div>
        </div>
      </div>
      {s.carried_over > 0 && (
        <div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-[rgba(245,158,11,0.08)] border border-[rgba(245,158,11,0.15)]">
          <span className="text-[10px] text-[#d97706]">+{s.carried_over} {t('vacay.carriedOver', { year: selectedYear - 1 })}</span>
        </div>
      )}
    </div>
  )
}