Skip to content

Commit 1872ba9

Browse files
committed
feat: add break handling functionality and enhance UI components for improved user interaction
1 parent d5d156d commit 1872ba9

11 files changed

Lines changed: 260 additions & 170 deletions

File tree

frontend/src/components/ClockInOut.tsx

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ export function ClockInOut() {
6767
});
6868
};
6969

70+
const handleBreak = () => {
71+
if (!todayEntry) {
72+
toast.error('Not clocked in', {
73+
description: 'You need to clock in first',
74+
});
75+
return;
76+
}
77+
78+
updateClockEntry(todayEntry.id, {
79+
breakTime: (todayEntry.breakTime || 0) + 5,
80+
});
81+
82+
toast.success('Break Started', {
83+
description: 'Take 5 minutes to recharge',
84+
});
85+
};
86+
7087
const getTodayTotalHours = () => {
7188
const todayEntries = clockEntries.filter((e) => e.date === today);
7289
let total = todayEntries.reduce((acc, entry) => {
@@ -118,29 +135,29 @@ export function ClockInOut() {
118135
<div className="max-w-7xl mx-auto space-y-6 p-4 md:p-6 pb-20">
119136
<DateNavigator selectedDate={selectedDate} changeDate={changeDate} isToday={isToday} />
120137

121-
{/* Main Clock Section */}
122-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
123-
<WorkClock
138+
{/* Main Work Clock Section - Full Width with Internal Grid */}
139+
<WorkClock
140+
todayEntry={todayEntry}
141+
isToday={isToday}
142+
todayTotal={todayTotal}
143+
onClockIn={handleClockIn}
144+
onClockOut={handleClockOut}
145+
onBreak={handleBreak}
146+
/>
147+
148+
{/* Info Panel & Timeline - Side by Side Below */}
149+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
150+
<InfoPanel
124151
todayEntry={todayEntry}
125152
isToday={isToday}
126153
todayTotal={todayTotal}
127-
onClockIn={handleClockIn}
128-
onClockOut={handleClockOut}
154+
selectedDayEntries={selectedDayEntries}
129155
/>
130156

131-
{/* Info Panel & Timeline */}
132-
<div className="space-y-4">
133-
<InfoPanel
134-
todayEntry={todayEntry}
135-
isToday={isToday}
136-
todayTotal={todayTotal}
137-
selectedDayEntries={selectedDayEntries}
138-
/>
139-
140-
<WorkTimeline entries={selectedDayEntries} />
141-
</div>
157+
<WorkTimeline entries={selectedDayEntries} />
142158
</div>
143159

160+
{/* Stats Grid - Full Width */}
144161
<StatsGrid
145162
isToday={isToday}
146163
todayTotal={todayTotal}

frontend/src/components/clock-in-out/DateNavigator.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ interface DateNavigatorProps {
1111

1212
export function DateNavigator({ selectedDate, changeDate, isToday }: DateNavigatorProps) {
1313
return (
14-
<Card className="border-2 backdrop-blur-xl bg-card/50">
14+
<Card className="border border-border/50 backdrop-blur-xl bg-card/50 shadow-none">
1515
<CardContent className="pt-6">
1616
<div className="flex items-center justify-between">
1717
<Button

frontend/src/components/clock-in-out/InfoPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function InfoPanel({ todayEntry, isToday, todayTotal, selectedDayEntries
1818
};
1919

2020
return (
21-
<Card className="border-2 backdrop-blur-xl bg-card/50">
21+
<Card className="border border-border/50 backdrop-blur-xl bg-card/50 shadow-none">
2222
<CardContent className="pt-6 space-y-6">
2323
<div>
2424
<p className="text-sm text-muted-foreground uppercase tracking-wider mb-2">

frontend/src/components/clock-in-out/StatsGrid.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,50 @@ interface StatsGridProps {
1111
export function StatsGrid({ isToday, todayTotal, weeklyHours, totalEntries }: StatsGridProps) {
1212
return (
1313
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
14-
<Card className="backdrop-blur-xl bg-card/50 border-2 hover:shadow-md transition-all group">
14+
<Card className="backdrop-blur-xl bg-card/50 border border-border/50 shadow-none">
1515
<CardContent className="pt-6">
1616
<div className="flex items-start justify-between mb-4">
17-
<div className="p-3 rounded-xl bg-gradient-to-br from-blue-500/20 to-blue-500/10">
18-
<Timer className="w-6 h-6 text-blue-500" />
17+
<div className="p-3 rounded-xl bg-gradient-to-br from-gray-500/20 to-gray-500/10">
18+
<Timer className="w-6 h-6 text-gray-500" />
1919
</div>
2020
</div>
2121
<div className="space-y-1">
2222
<p className="text-sm text-muted-foreground">Today's Hours</p>
23-
<div className="text-4xl bg-gradient-to-br from-blue-500 to-blue-600 bg-clip-text text-transparent tabular-nums">
23+
<div className="text-4xl bg-gradient-to-br from-gray-500 to-gray-600 bg-clip-text text-transparent tabular-nums">
2424
{isToday ? `${todayTotal.hours}h ${todayTotal.minutes}m` : '0h 0m'}
2525
</div>
2626
<p className="text-xs text-muted-foreground">Total work time</p>
2727
</div>
2828
</CardContent>
2929
</Card>
3030

31-
<Card className="backdrop-blur-xl bg-card/50 border-2 hover:shadow-md transition-all group">
31+
<Card className="backdrop-blur-xl bg-card/50 border border-border/50 shadow-none">
3232
<CardContent className="pt-6">
3333
<div className="flex items-start justify-between mb-4">
34-
<div className="p-3 rounded-xl bg-gradient-to-br from-purple-500/20 to-purple-500/10">
35-
<Calendar className="w-6 h-6 text-purple-500" />
34+
<div className="p-3 rounded-xl bg-gradient-to-br from-gray-500/20 to-gray-500/10">
35+
<Calendar className="w-6 h-6 text-gray-500" />
3636
</div>
3737
</div>
3838
<div className="space-y-1">
3939
<p className="text-sm text-muted-foreground">Weekly Hours</p>
40-
<div className="text-4xl bg-gradient-to-br from-purple-500 to-purple-600 bg-clip-text text-transparent tabular-nums">
40+
<div className="text-4xl bg-gradient-to-br from-gray-500 to-gray-600 bg-clip-text text-transparent tabular-nums">
4141
{weeklyHours}
4242
</div>
4343
<p className="text-xs text-muted-foreground">Last 7 days</p>
4444
</div>
4545
</CardContent>
4646
</Card>
4747

48-
<Card className="backdrop-blur-xl bg-card/50 border-2 hover:shadow-md transition-all group">
48+
<Card className="backdrop-blur-xl bg-card/50 border border-border/50 shadow-none">
4949
<CardContent className="pt-6">
5050
<div className="flex items-start justify-between mb-4">
51-
<div className="p-3 rounded-xl bg-gradient-to-br from-green-500/20 to-green-500/10">
52-
<Clock className="w-6 h-6 text-green-500" />
51+
<div className="p-3 rounded-xl bg-gradient-to-br from-gray-500/20 to-gray-500/10">
52+
<Clock className="w-6 h-6 text-gray-500" />
5353
</div>
5454
</div>
5555
<div className="space-y-1">
5656
<p className="text-sm text-muted-foreground">Total Entries</p>
57-
<div className="text-4xl bg-gradient-to-br from-green-500 to-green-600 bg-clip-text text-transparent tabular-nums">
57+
<div className="text-4xl bg-gradient-to-br from-gray-500 to-gray-600 bg-clip-text text-transparent tabular-nums">
5858
{totalEntries}
5959
</div>
6060
<p className="text-xs text-muted-foreground">All time</p>
Lines changed: 96 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { Card, CardContent } from '@/components/ui/card';
21
import { Button } from '@/components/ui/button';
3-
import { LogIn, LogOut } from 'lucide-react';
2+
import { LogIn, LogOut, Pause } from 'lucide-react';
43
import type { ClockEntry } from '@/store/useHistoryStore';
54

65
interface WorkClockProps {
@@ -9,6 +8,7 @@ interface WorkClockProps {
98
todayTotal: { hours: number; minutes: number; seconds: number };
109
onClockIn: () => void;
1110
onClockOut: () => void;
11+
onBreak?: () => void;
1212
}
1313

1414
export function WorkClock({
@@ -17,115 +17,112 @@ export function WorkClock({
1717
todayTotal,
1818
onClockIn,
1919
onClockOut,
20+
onBreak,
2021
}: WorkClockProps) {
21-
return (
22-
<Card className="border-2 backdrop-blur-xl bg-gradient-to-br from-card/80 via-card/50 to-card/80 shadow-x overflow-hidden relative">
23-
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-primary/10"></div>
24-
25-
<CardContent className="relative z-10 pt-8 pb-8">
26-
<div className="flex flex-col items-center justify-center space-y-6">
27-
<div className="relative w-64 h-64 sm:w-72 sm:h-72">
28-
<svg className="w-full h-full transform -rotate-90">
29-
<defs>
30-
<linearGradient id="clockGradient" x1="0%" y1="0%" x2="100%" y2="100%">
31-
<stop offset="0%" stopColor="#22c55e" stopOpacity="0.8" />
32-
<stop offset="100%" stopColor="#16a34a" stopOpacity="1" />
33-
</linearGradient>
34-
</defs>
35-
36-
{[...Array(60)].map((_, i) => {
37-
const angle = (i * 6 * Math.PI) / 180;
38-
const isHourMark = i % 5 === 0;
39-
const outerRadius = 136;
40-
const innerRadius = isHourMark ? 126 : 130;
41-
const x1 = 144 + outerRadius * Math.cos(angle);
42-
const y1 = 144 + outerRadius * Math.sin(angle);
43-
const x2 = 144 + innerRadius * Math.cos(angle);
44-
const y2 = 144 + innerRadius * Math.sin(angle);
22+
const timeString = `${String(todayTotal.hours).padStart(2, '0')}:${String(todayTotal.minutes).padStart(2, '0')}:${String(todayTotal.seconds).padStart(2, '0')}`;
4523

46-
return (
47-
<line
48-
key={i}
49-
x1={x1}
50-
y1={y1}
51-
x2={x2}
52-
y2={y2}
53-
stroke="currentColor"
54-
strokeWidth={isHourMark ? '2' : '1'}
55-
className="text-muted-foreground/30"
56-
/>
57-
);
58-
})}
59-
60-
{/* Background circle */}
61-
<circle
62-
cx="144"
63-
cy="144"
64-
r="110"
65-
stroke="currentColor"
66-
strokeWidth="8"
67-
fill="none"
68-
className="text-muted/10"
69-
/>
24+
return (
25+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
26+
{/* Main Time Display - Hero Section */}
27+
<div className="lg:col-span-2 relative overflow-hidden rounded-xl bg-surface-container-low border border-outline-variant/10 p-10 flex flex-col justify-between min-h-[360px]">
28+
{/* Ambient Glow Background */}
29+
<div className="absolute -top-20 -right-20 w-80 h-80 bg-primary/10 blur-[100px] rounded-full"></div>
7030

71-
{/* Progress circle */}
72-
{todayEntry && isToday && (
73-
<circle
74-
cx="144"
75-
cy="144"
76-
r="110"
77-
stroke="url(#clockGradient)"
78-
strokeWidth="8"
79-
fill="none"
80-
strokeDasharray={`${2 * Math.PI * 110}`}
81-
strokeDashoffset={`${2 * Math.PI * 110 * (1 - (todayTotal.hours % 24) / 24)}`}
82-
className="transition-all duration-1000"
83-
strokeLinecap="round"
84-
/>
85-
)}
86-
</svg>
31+
<div className="relative z-10">
32+
{/* Status Indicator */}
33+
<div className="flex items-center gap-3 mb-8">
34+
<div className="w-3 h-3 bg-primary rounded-full animate-pulse shadow-[0_0_10px_rgba(92,253,128,0.8)]"></div>
35+
<span className="text-primary font-label tracking-widest uppercase text-xs font-bold">
36+
{todayEntry && isToday ? 'Currently Clocked In' : 'Ready to Clock In'}
37+
</span>
38+
</div>
8739

88-
{/* Center content */}
89-
<div className="absolute inset-0 flex items-center justify-center flex-col">
90-
<div className="text-center space-y-1">
91-
<p className="text-xs text-muted-foreground uppercase tracking-wider">
92-
{todayEntry && isToday ? 'Time at Work' : 'Left Time'}
93-
</p>
94-
<div className="text-xl sm:text-2xl tabular-nums text-green-500">
95-
{todayEntry && isToday
96-
? `${String(todayTotal.hours).padStart(2, '0')}h ${String(todayTotal.minutes).padStart(2, '0')}m ${String(todayTotal.seconds).padStart(2, '0')}s`
97-
: 'Online'}
98-
</div>
99-
</div>
100-
</div>
40+
{/* Large Time Display */}
41+
<div className="flex flex-col mb-8">
42+
<span className="text-on-surface-variant font-label uppercase tracking-[0.2em] mb-2 text-xs">
43+
{todayEntry && isToday ? 'Elapsed Professional Time' : 'Total Time'}
44+
</span>
45+
<h3 className="font-headline text-[4rem] sm:text-[5rem] lg:text-[6rem] font-bold leading-none tracking-tighter text-on-background">
46+
{todayEntry && isToday ? timeString.slice(0, 5) : '00:00'}
47+
<span className="text-primary/50 text-2xl sm:text-3xl ml-2 tracking-normal font-medium">
48+
:{todayEntry && isToday ? timeString.slice(6) : '00'}
49+
</span>
50+
</h3>
10151
</div>
52+
</div>
10253

103-
{/* Clock In/Out Buttons */}
104-
{isToday && (
105-
<div className="flex flex-col sm:flex-row gap-3 w-full max-w-md">
54+
{/* Action Buttons */}
55+
{isToday && (
56+
<div className="relative z-10 flex flex-col sm:flex-row items-center gap-4">
57+
{todayEntry ? (
58+
<>
59+
<Button
60+
onClick={onClockOut}
61+
className="px-10 py-5 bg-primary text-on-primary-container font-headline font-bold text-lg rounded-xl flex items-center gap-3 transition-transform duration-150 border border-primary/20"
62+
>
63+
<LogOut className="w-6 h-6" />
64+
Clock Out
65+
</Button>
66+
<Button
67+
onClick={onBreak}
68+
variant="outline"
69+
className="px-8 py-5 border border-outline-variant/30 text-secondary font-headline font-bold text-lg rounded-xl flex items-center gap-3 hover:bg-secondary/5 transition-all"
70+
>
71+
<Pause className="w-5 h-5" />
72+
Take a Break
73+
</Button>
74+
</>
75+
) : (
10676
<Button
107-
size="lg"
10877
onClick={onClockIn}
109-
disabled={!!todayEntry}
110-
className="flex-1 h-14 border-2 border-gray-500/20 text-base gap-3 shadow-green-500/15 hover:shadow-x hover:border-purple-500/20 transition-radius disabled:opacity-50"
78+
className="px-10 py-5 bg-primary text-on-primary-container font-headline font-bold text-lg rounded-xl flex items-center gap-3 transition-transform duration-150 active:scale-95 shadow-lg shadow-primary/20"
11179
>
112-
<LogIn className="w-5 h-5" />
80+
<LogIn className="w-6 h-6" />
11381
Clock In
11482
</Button>
115-
<Button
116-
size="lg"
117-
onClick={onClockOut}
118-
disabled={!todayEntry}
119-
variant="secondary"
120-
className="flex-1 h-14 border-2 border-gray-500/20 text-base gap-3 shadow-red-500/15 hover:shadow-x hover:border-red-500/20 transition-radius disabled:opacity-40"
121-
>
122-
<LogOut className="w-5 h-5" />
123-
Clock Out
124-
</Button>
83+
)}
84+
</div>
85+
)}
86+
</div>
87+
88+
{/* Side Card: Daily Pulse - Corner Position */}
89+
<div className="lg:col-span-1 flex flex-col gap-4">
90+
<div className="bg-surface-container-highest/40 backdrop-blur-md rounded-xl p-8 border border-outline-variant/10 flex-1 flex flex-col justify-center">
91+
<h4 className="text-on-surface-variant font-label uppercase tracking-widest text-xs mb-6">Daily Pulse</h4>
92+
<div className="space-y-6">
93+
<div className="flex items-center justify-between">
94+
<span className="text-on-surface/80 text-sm">Session Duration</span>
95+
<span className="font-headline font-medium text-lg text-secondary-design">
96+
{todayEntry && isToday
97+
? `${String(Math.floor((new Date().getTime() - new Date(todayEntry.clockIn).getTime()) / (1000 * 60))).padStart(2, '0')} min`
98+
: '--:-- --'}
99+
</span>
100+
</div>
101+
<div className="flex items-center justify-between">
102+
<span className="text-on-surface/80 text-sm">Status</span>
103+
<span className="font-headline font-medium text-lg text-tertiary">
104+
{todayEntry && isToday && todayEntry.clockOut ? 'Clocked Out' : todayEntry ? 'Online' : 'Offline'}
105+
</span>
125106
</div>
126-
)}
107+
<div className="pt-4 border-t border-outline-variant/10">
108+
<div className="flex items-center justify-between text-sm text-on-surface-variant mb-2">
109+
<span>Daily Progress</span>
110+
<span>
111+
{todayEntry && isToday ? `${Math.min(Math.floor((todayTotal.hours / 8) * 100), 100)}%` : '0%'}
112+
</span>
113+
</div>
114+
<div className="h-1.5 w-full bg-surface-container-high rounded-full overflow-hidden">
115+
<div
116+
className="h-full bg-primary rounded-full transition-all duration-500"
117+
style={{
118+
width: `${todayEntry && isToday ? Math.min((todayTotal.hours / 8) * 100, 100) : 0}%`,
119+
}}
120+
></div>
121+
</div>
122+
</div>
123+
</div>
127124
</div>
128-
</CardContent>
129-
</Card>
125+
</div>
126+
</div>
130127
);
131128
}

0 commit comments

Comments
 (0)