Skip to content

Commit 58bc1bf

Browse files
committed
feat: Display leave requests alongside attendance records on staff history page and add attendance backfill script.
1 parent 93f7eb7 commit 58bc1bf

File tree

1 file changed

+115
-61
lines changed

1 file changed

+115
-61
lines changed

app/staff/history/page.tsx

Lines changed: 115 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,24 @@ type AttendanceRecord = {
2424
total_hours: number | null
2525
status?: string // 'Complete' | 'Pending'
2626
created_at: string
27+
type: 'attendance'
28+
}
29+
30+
type LeaveRecord = {
31+
id: string
32+
user_id: string
33+
leave_type: string
34+
start_date: string
35+
end_date: string
36+
reason: string
37+
status: 'pending' | 'approved' | 'rejected'
38+
created_at: string
39+
type: 'leave'
2740
}
2841

2942
export default function StaffHistoryPage() {
3043
const { user, loading: authLoading } = useAuth()
31-
const [history, setHistory] = useState<AttendanceRecord[]>([])
44+
const [history, setHistory] = useState<(AttendanceRecord | LeaveRecord)[]>([])
3245
const [loading, setLoading] = useState(true)
3346
const [stats, setStats] = useState({
3447
totalHours: 0,
@@ -43,20 +56,38 @@ export default function StaffHistoryPage() {
4356

4457
const fetchData = async () => {
4558
try {
46-
const { data, error } = await supabase
59+
// Fetch Attendance
60+
const { data: attendanceData, error: attendanceError } = await supabase
4761
.from("attendance_logs")
4862
.select("*")
4963
.eq("user_id", user.id)
50-
.order("check_in", { ascending: false })
5164

52-
if (error) throw error
65+
if (attendanceError) throw attendanceError
66+
67+
// Fetch Leaves
68+
const { data: leaveData, error: leaveError } = await supabase
69+
.from("leave_requests")
70+
.select("*")
71+
.eq("user_id", user.id)
72+
.neq("status", "rejected") // Only show pending/approved
73+
74+
if (leaveError) throw leaveError
5375

54-
const records = data || []
55-
setHistory(records)
76+
const attendanceRecords: AttendanceRecord[] = (attendanceData || []).map(r => ({ ...r, type: 'attendance' }))
77+
const leaveRecords: LeaveRecord[] = (leaveData || []).map(r => ({ ...r, type: 'leave' }))
78+
79+
// Merge and Sort
80+
const mergedRecords = [...attendanceRecords, ...leaveRecords].sort((a, b) => {
81+
const dateA = a.type === 'attendance' ? new Date(a.check_in) : new Date(a.start_date)
82+
const dateB = b.type === 'attendance' ? new Date(b.check_in) : new Date(b.start_date)
83+
return dateB.getTime() - dateA.getTime() // Descending
84+
})
5685

57-
// Calculate stats
58-
const totalHours = records.reduce((acc, curr) => acc + (curr.total_hours || 0), 0)
59-
const daysPresent = records.length
86+
setHistory(mergedRecords)
87+
88+
// Calculate stats (only from attendance)
89+
const totalHours = attendanceRecords.reduce((acc, curr) => acc + (curr.total_hours || 0), 0)
90+
const daysPresent = attendanceRecords.length
6091
const averageHours = daysPresent > 0 ? totalHours / daysPresent : 0
6192

6293
setStats({
@@ -92,17 +123,9 @@ export default function StaffHistoryPage() {
92123
Attendance <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-400">History</span>
93124
</h1>
94125
<p className="text-zinc-400">
95-
View and track your past attendance records.
126+
View and track your past attendance and leave records.
96127
</p>
97128
</div>
98-
{/* <div className="flex gap-2">
99-
<Button variant="outline" className="border-zinc-800 bg-zinc-900/50 hover:bg-zinc-800 text-zinc-300">
100-
<Filter className="mr-2 h-4 w-4" /> Filter
101-
</Button>
102-
<Button variant="outline" className="border-zinc-800 bg-zinc-900/50 hover:bg-zinc-800 text-zinc-300">
103-
<Download className="mr-2 h-4 w-4" /> Export
104-
</Button>
105-
</div> */}
106129
</header>
107130

108131
{/* Stats Overview */}
@@ -167,55 +190,86 @@ export default function StaffHistoryPage() {
167190
{history.length === 0 ? (
168191
<tr>
169192
<td colSpan={5} className="px-6 py-12 text-center text-zinc-500">
170-
No attendance records found.
193+
No records found.
171194
</td>
172195
</tr>
173196
) : (
174197
history.map((record) => {
175-
const date = new Date(record.check_in).toLocaleDateString(undefined, {
176-
weekday: 'short',
177-
year: 'numeric',
178-
month: 'short',
179-
day: 'numeric'
180-
})
181-
const checkInTime = new Date(record.check_in).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
182-
const checkOutTime = record.check_out
183-
? new Date(record.check_out).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
184-
: '-'
185-
186-
const isComplete = !!record.check_out
187-
188-
return (
189-
<tr key={record.id} className="group hover:bg-zinc-800/30 transition-colors">
190-
<td className="px-6 py-4 text-zinc-300 font-medium">{date}</td>
191-
<td className="px-6 py-4 text-zinc-400">{checkInTime}</td>
192-
<td className="px-6 py-4 text-zinc-400">{checkOutTime}</td>
193-
<td className="px-6 py-4">
194-
<span className={`px-2.5 py-1 rounded font-mono text-xs font-medium ${record.total_hours && record.total_hours >= 8
198+
if (record.type === 'leave') {
199+
// Render Leave Row
200+
const r = record as LeaveRecord
201+
const date = new Date(r.start_date).toLocaleDateString(undefined, {
202+
weekday: 'short',
203+
year: 'numeric',
204+
month: 'short',
205+
day: 'numeric'
206+
})
207+
return (
208+
<tr key={r.id} className="group hover:bg-zinc-800/30 transition-colors bg-blue-500/5 border-l-2 border-blue-500/20">
209+
<td className="px-6 py-4 text-zinc-300 font-medium">{date}</td>
210+
<td className="px-6 py-4 text-zinc-500">-</td>
211+
<td className="px-6 py-4 text-zinc-500">-</td>
212+
<td className="px-6 py-4">
213+
<span className="px-2.5 py-1 rounded font-mono text-xs font-medium bg-blue-500/10 text-blue-400 border border-blue-500/20">
214+
{r.leave_type} Leave
215+
</span>
216+
</td>
217+
<td className="px-6 py-4">
218+
<div className="flex items-center gap-1.5 text-xs font-medium text-blue-400">
219+
<Calendar className="w-4 h-4" />
220+
On Leave
221+
</div>
222+
</td>
223+
</tr>
224+
)
225+
} else {
226+
// Render Attendance Row
227+
const r = record as AttendanceRecord
228+
const date = new Date(r.check_in).toLocaleDateString(undefined, {
229+
weekday: 'short',
230+
year: 'numeric',
231+
month: 'short',
232+
day: 'numeric'
233+
})
234+
const checkInTime = new Date(r.check_in).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
235+
const checkOutTime = r.check_out
236+
? new Date(r.check_out).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
237+
: '-'
238+
239+
const isComplete = !!r.check_out
240+
241+
return (
242+
<tr key={r.id} className="group hover:bg-zinc-800/30 transition-colors">
243+
<td className="px-6 py-4 text-zinc-300 font-medium">{date}</td>
244+
<td className="px-6 py-4 text-zinc-400">{checkInTime}</td>
245+
<td className="px-6 py-4 text-zinc-400">{checkOutTime}</td>
246+
<td className="px-6 py-4">
247+
<span className={`px-2.5 py-1 rounded font-mono text-xs font-medium ${r.total_hours && r.total_hours >= 8
195248
? "bg-green-500/10 text-green-400 border border-green-500/20"
196249
: "bg-blue-500/10 text-blue-400 border border-blue-500/20"
197-
}`}>
198-
{record.total_hours ? `${record.total_hours.toFixed(2)}h` : '-'}
199-
</span>
200-
</td>
201-
<td className="px-6 py-4">
202-
<div className={`flex items-center gap-1.5 text-xs font-medium ${isComplete ? "text-green-400" : "text-yellow-400"
203-
}`}>
204-
{isComplete ? (
205-
<>
206-
<CheckCircle2 className="w-4 h-4" />
207-
Completed
208-
</>
209-
) : (
210-
<>
211-
<Clock className="w-4 h-4" />
212-
Active
213-
</>
214-
)}
215-
</div>
216-
</td>
217-
</tr>
218-
)
250+
}`}>
251+
{r.total_hours ? `${r.total_hours.toFixed(2)}h` : '-'}
252+
</span>
253+
</td>
254+
<td className="px-6 py-4">
255+
<div className={`flex items-center gap-1.5 text-xs font-medium ${isComplete ? "text-green-400" : "text-yellow-400"
256+
}`}>
257+
{isComplete ? (
258+
<>
259+
<CheckCircle2 className="w-4 h-4" />
260+
Completed
261+
</>
262+
) : (
263+
<>
264+
<Clock className="w-4 h-4" />
265+
Active
266+
</>
267+
)}
268+
</div>
269+
</td>
270+
</tr>
271+
)
272+
}
219273
})
220274
)}
221275
</tbody>

0 commit comments

Comments
 (0)