Break Tracking
This guide covers how to record breaks during your shift, including automatic break calculations, break policies, and best practices for accurate time tracking.
Overview
What is Break Tracking?
Break tracking ensures accurate working hours calculation:
- Break Recording - Log breaks taken during shifts
- Automatic Deduction - Breaks deducted from total hours
- Policy Compliance - Ensure legal break requirements met
- Accurate Payroll - Correct pay calculation
Break Types
| Type | Typical Duration | Paid/Unpaid |
|---|---|---|
| Lunch Break | 30-60 minutes | Usually unpaid |
| Tea/Coffee Break | 10-15 minutes | Usually paid |
| Rest Break | 20 minutes | Legally required |
| Personal Break | Variable | Depends on policy |
Recording Breaks
Method 1: At Clock Out
The simplest method is recording total break time when clocking out:
- Click Clock Out on the Time Clock widget
- Enter total break time in minutes
- Add notes if needed
- Confirm to save
// Clock out with break duration
const response = await fetch(`/api/staff/timesheet/entries/${entryId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clock_out_time: '2025-01-14T17:30:00Z',
break_duration: 45, // 45 minutes total break
notes: '30 min lunch + 15 min afternoon break'
})
})
Method 2: Start/End Break
For real-time break tracking:
- Click Start Break when taking a break
- The timer shows break duration
- Click End Break when returning
- Break time automatically recorded
Method 3: Edit After Clock Out
If you forgot to record breaks:
- Go to Timesheet → View Entries
- Find the relevant entry
- Click Edit
- Add break duration
- Save changes
Break Duration Validation
Minimum and Maximum Limits
// Source: src/app/api/staff/timesheet/entries/route.ts:70-78
const breakDurationSchema = z.number()
.min(0, 'Break duration cannot be negative')
.max(480, 'Break duration cannot exceed 8 hours')
.optional()
Validation Rules
| Rule | Limit | Reason |
|---|---|---|
| Minimum | 0 minutes | Cannot be negative |
| Maximum | 480 minutes (8 hours) | Cannot exceed shift length |
| Shift Check | < Total shift | Break must be less than shift |
Break vs Shift Validation
// Ensure break doesn't exceed shift duration
const validateBreakDuration = (
clockIn: string,
clockOut: string,
breakMinutes: number
): boolean => {
const shiftDuration = moment(clockOut).diff(moment(clockIn), 'minutes')
if (breakMinutes >= shiftDuration) {
throw new Error('Break duration cannot exceed shift duration')
}
return true
}
Automatic Break Rules
Policy-Based Breaks
Some companies have automatic break policies:
// Automatic break calculation example
const calculateAutoBreak = (shiftHours: number): number => {
// Example policy: 30 min break for shifts over 6 hours
if (shiftHours >= 6) {
return 30 // 30 minutes
}
// 15 min break for shifts 4-6 hours
if (shiftHours >= 4) {
return 15
}
return 0 // No automatic break
}
UK Legal Requirements
| Shift Length | Required Break | Notes |
|---|---|---|
| < 6 hours | None required | At employer discretion |
| 6+ hours | 20 minutes minimum | Uninterrupted rest break |
| Long shifts | Additional breaks | As per policy |
Break Time Calculation
How Breaks Affect Total Hours
// Source: src/app/api/staff/timesheet/entries/route.ts:120-145
// Total hours = Shift duration - Break duration
const calculateWorkingHours = (entry: TimeEntry): number => {
const clockIn = moment.tz(entry.clock_in_time, UK_TIMEZONE)
const clockOut = moment.tz(entry.clock_out_time, UK_TIMEZONE)
// Raw shift duration in hours
const shiftHours = clockOut.diff(clockIn, 'hours', true)
// Break duration in hours
const breakHours = (entry.break_duration || 0) / 60
// Net working hours
const workingHours = shiftHours - breakHours
return Math.round(workingHours * 100) / 100
}
Example Calculations
| Shift | Clock In | Clock Out | Raw Hours | Break | Net Hours |
|---|---|---|---|---|---|
| Morning | 09:00 | 13:00 | 4.00 | 0 min | 4.00 |
| Full Day | 09:00 | 17:30 | 8.50 | 30 min | 8.00 |
| Long Shift | 08:00 | 20:00 | 12.00 | 60 min | 11.00 |
Break Recording Interface
Time Clock Widget
// Break recording component structure
interface BreakRecording {
entryId: string
breakDuration: number // minutes
breakNotes?: string
}
// Display break input
const BreakInput = ({ value, onChange }) => (
<div className="space-y-2">
<label className="text-sm font-medium">Break Duration (minutes)</label>
<input
type="number"
min={0}
max={480}
value={value}
onChange={(e) => onChange(parseInt(e.target.value))}
className="form-input"
placeholder="Enter break time in minutes"
/>
<p className="text-xs text-secondary-text">
Total break time taken during this shift
</p>
</div>
)
Quick Break Presets
| Preset | Duration | Use Case |
|---|---|---|
| No Break | 0 min | Short shifts |
| Tea Break | 15 min | Quick break |
| Lunch | 30 min | Standard lunch |
| Extended Lunch | 60 min | Long lunch break |
Multiple Breaks
Recording Multiple Breaks
When taking multiple breaks, record the total:
// Example: Recording multiple breaks
const breaks = [
{ type: 'morning tea', duration: 15 },
{ type: 'lunch', duration: 30 },
{ type: 'afternoon tea', duration: 15 }
]
const totalBreakMinutes = breaks.reduce((sum, b) => sum + b.duration, 0)
// Result: 60 minutes total
// Record total in time entry
await updateTimeEntry(entryId, {
break_duration: totalBreakMinutes,
notes: 'Morning tea (15), Lunch (30), Afternoon tea (15)'
})
Break Notes
Use notes to detail break breakdown:
| Good Notes | Bad Notes |
|---|---|
| "30 min lunch, 15 min afternoon break" | "45 min" |
| "Two 20-minute breaks" | "breaks" |
| "Extended lunch - 60 min (approved)" | "" |
Break Policies
Company Break Policies
Companies may have specific break policies:
// Break policy configuration
interface BreakPolicy {
minimumShiftForBreak: number // hours
mandatoryBreakDuration: number // minutes
paidBreaks: boolean
maxBreakDuration: number // minutes
autoApplyBreaks: boolean
}
// Example GP Practice policy
const gpPracticeBreakPolicy: BreakPolicy = {
minimumShiftForBreak: 6,
mandatoryBreakDuration: 30,
paidBreaks: false,
maxBreakDuration: 60,
autoApplyBreaks: false
}
Industry-Specific Guidelines
| Industry | Typical Policy |
|---|---|
| GP Practice | 30 min unpaid lunch for 6+ hour shifts |
| Restaurant | Staggered breaks during service |
| Dental | Between patient appointments |
| Office | Flexible break timing |
Editing Break Records
Modifying Break Duration
To edit a previously recorded break:
// Update break duration
const updateBreak = async (entryId: string, newBreakMinutes: number) => {
const response = await fetch(`/api/staff/timesheet/entries/${entryId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
break_duration: newBreakMinutes,
notes: 'Updated break duration'
})
})
return response.json()
}
Edit Restrictions
| Entry Status | Can Edit Break? |
|---|---|
| Draft | ✅ Yes |
| Submitted | ❌ No - recall first |
| Approved | ❌ No - contact manager |
| Rejected | ✅ Yes - then resubmit |
Break Time Display
Timesheet View
| Column | Format | Example |
|---|---|---|
| Break | Minutes | 30 min |
| Break | Hours:Minutes | 0:30 |
| Percentage | Of shift | 5.9% |
Summary Statistics
// Calculate break statistics for period
const calculateBreakStats = (entries: TimeEntry[]) => {
const totalBreakMinutes = entries.reduce(
(sum, e) => sum + (e.break_duration || 0), 0
)
const averageBreak = totalBreakMinutes / entries.length
return {
totalBreakMinutes,
averageBreakMinutes: Math.round(averageBreak),
totalBreakHours: Math.round(totalBreakMinutes / 60 * 10) / 10
}
}
Best Practices
For Accurate Break Recording
- Record Immediately - Log breaks as you take them
- Be Precise - Round to nearest 5 minutes
- Include All Breaks - Tea, lunch, personal
- Add Context - Note reason for extended breaks
- Review Daily - Check entries before end of day
Break Timing Tips
| Tip | Benefit |
|---|---|
| Set break reminders | Don't forget to log |
| Use presets | Quick recording |
| Check policy | Ensure compliance |
| Track patterns | Optimise scheduling |
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| "Break exceeds shift" | Break longer than shift | Reduce break duration |
| Break not saved | Form not submitted | Click save/confirm |
| Wrong break recorded | Entry error | Edit the time entry |
| Break missing from hours | Not included | Add break duration |
Error Messages
| Error | Meaning | Action |
|---|---|---|
| "Break duration cannot be negative" | Negative value entered | Enter positive number |
| "Break duration cannot exceed 8 hours" | Value too large | Check break minutes |
| "Break duration exceeds shift" | Break > shift length | Reduce break time |
| "Entry is submitted" | Cannot edit submitted | Recall entry first |
Compliance Considerations
Legal Break Requirements (UK)
| Requirement | Details |
|---|---|
| 6+ hour shifts | 20 minutes uninterrupted break |
| Young workers | 30 minutes if shift > 4.5 hours |
| Workplace rest | 11 consecutive hours between shifts |
| Weekly rest | 24 hours uninterrupted per week |
Recording for Compliance
// Check legal break compliance
const checkBreakCompliance = (entry: TimeEntry): ComplianceResult => {
const shiftHours = calculateShiftHours(entry)
const breakMinutes = entry.break_duration || 0
const warnings = []
// Check 6+ hour shift has 20 min break
if (shiftHours >= 6 && breakMinutes < 20) {
warnings.push('Shifts over 6 hours require minimum 20 minute break')
}
return {
compliant: warnings.length === 0,
warnings
}
}
Related Documentation
- Clock In/Out - Start and end shifts
- Viewing Timesheet - Review hours
- Submitting Timesheet - Submit for approval
- Dashboard - Staff dashboard
Source Files:
src/app/api/staff/timesheet/entries/route.ts- Time entries APIsrc/lib/validation/timesheet.schemas.ts- Break validation schemassrc/components/staff/BreakRecorder.tsx- Break recording componentsrc/lib/utils/time-calculations.ts- Time calculation utilities