Shift Details
This guide covers how to view detailed information about your scheduled shifts, including times, location, type, and any special instructions.
Overview
What are Shift Details?
Shift details provide comprehensive information about each scheduled shift:
- Timing - Start and end times with duration
- Location - Room or facility assignment
- Type - Shift category (Standard, Night, etc.)
- Status - Confirmation status
- Notes - Special instructions or information
Accessing Shift Details
- Navigate to My Schedule in Staff Portal
- Click on any shift in the calendar
- The shift details panel opens
Shift Information
Core Details
| Field | Description | Example |
|---|---|---|
| Date | Shift date | 14/01/2025 |
| Start Time | When shift begins | 09:00 |
| End Time | When shift ends | 17:30 |
| Duration | Total shift length | 8h 30m |
| Room | Assigned location | Consultation Room 1 |
| Type | Shift category | Standard |
| Status | Confirmation state | Confirmed |
Shift Data Structure
// Shift detail structure
interface ShiftDetail {
id: string
company_id: string
staff_id: string
room_id: string | null
// Timing
start_time: string // ISO datetime
end_time: string // ISO datetime
duration_hours: number // Calculated
// Classification
shift_type: ShiftType
status: ShiftStatus
// Additional info
notes: string | null
created_at: string
updated_at: string
// Relations
room: Room | null
staff: Staff
}
Time Information
Time Display
All times shown in UK timezone (Europe/London):
// Format shift times for display
const formatShiftTimes = (shift: Shift) => {
const start = moment.tz(shift.start_time, UK_TIMEZONE)
const end = moment.tz(shift.end_time, UK_TIMEZONE)
return {
date: start.format('DD/MM/YYYY'),
dayName: start.format('dddd'),
startTime: start.format('HH:mm'),
endTime: end.format('HH:mm'),
duration: calculateDuration(start, end)
}
}
Duration Calculation
// Calculate shift duration
const calculateDuration = (start: moment.Moment, end: moment.Moment): string => {
const totalMinutes = end.diff(start, 'minutes')
const hours = Math.floor(totalMinutes / 60)
const minutes = totalMinutes % 60
if (minutes === 0) {
return `${hours}h`
}
return `${hours}h ${minutes}m`
}
Duration Examples
| Start | End | Duration |
|---|---|---|
| 09:00 | 17:00 | 8h |
| 09:00 | 17:30 | 8h 30m |
| 08:00 | 20:00 | 12h |
| 22:00 | 06:00 (+1) | 8h |
Room Assignment
Room Details
Each shift may have an assigned room:
// Room information in shift
{
room: {
id: "room-uuid",
name: "Consultation Room 1",
room_type: {
id: "type-uuid",
name: "Consultation",
color: "#3B82F6"
},
capacity: 2,
description: "Ground floor, wheelchair accessible"
}
}
Room Display
| Element | Description |
|---|---|
| Name | Room identifier |
| Type | Room category |
| Location | Floor/building info |
| Capacity | Maximum occupancy |
No Room Assigned
If no room is assigned:
// Handle unassigned room
const RoomDisplay = ({ room }) => {
if (!room) {
return (
<div className="text-secondary-text italic">
No room assigned - check with manager
</div>
)
}
return (
<div>
<span className="font-medium">{room.name}</span>
{room.room_type && (
<span className="ml-2 text-sm text-secondary-text">
({room.room_type.name})
</span>
)}
</div>
)
}
Shift Types
Available Types
| Type | Description | Typical Hours |
|---|---|---|
| Standard | Regular day shift | 09:00 - 17:30 |
| Morning | Early shift | 06:00 - 14:00 |
| Evening | Late shift | 14:00 - 22:00 |
| Night | Overnight shift | 22:00 - 06:00 |
| On-Call | Standby duty | Variable |
| Training | Training/development | Variable |
Type Display
// Shift type badge
const ShiftTypeBadge = ({ type }: { type: ShiftType }) => {
const colours = {
standard: 'bg-blue-500',
morning: 'bg-amber-500',
evening: 'bg-purple-500',
night: 'bg-indigo-500',
on_call: 'bg-orange-500',
training: 'bg-green-500'
}
return (
<span className={`px-2 py-1 rounded text-white text-sm ${colours[type]}`}>
{formatShiftType(type)}
</span>
)
}
Shift Status
Status Values
| Status | Meaning | Action Required |
|---|---|---|
| Confirmed | Shift is scheduled | Report for duty |
| Draft | Tentative schedule | Await confirmation |
| Cancelled | Shift was removed | Do not report |
Status Display
// Status indicator component
const ShiftStatusBadge = ({ status }: { status: ShiftStatus }) => {
const config = {
confirmed: {
colour: 'bg-green-500',
icon: Check,
label: 'Confirmed'
},
draft: {
colour: 'bg-gray-500',
icon: Clock,
label: 'Draft'
},
cancelled: {
colour: 'bg-red-500',
icon: X,
label: 'Cancelled'
}
}
const { colour, icon: Icon, label } = config[status]
return (
<div className={`flex items-center gap-2 px-3 py-1 rounded ${colour} text-white`}>
<Icon className="w-4 h-4" />
<span>{label}</span>
</div>
)
}
Status Transitions
Draft → Confirmed → (may be) Cancelled
↘ (may be) Cancelled
Shift Notes
Viewing Notes
Notes contain special instructions:
// Notes display
const ShiftNotes = ({ notes }) => {
if (!notes) return null
return (
<div className="mt-4 p-3 bg-white/5 rounded-lg">
<div className="flex items-center gap-2 text-sm font-medium">
<Info className="w-4 h-4" />
Notes
</div>
<p className="mt-2 text-secondary-text">{notes}</p>
</div>
)
}
Common Note Types
| Type | Example |
|---|---|
| Task Notes | "Cover reception 12:00-13:00" |
| Equipment | "Bring laptop for training" |
| Meeting | "Team meeting at 09:30" |
| Special | "Early finish - clinic closes 16:00" |
Shift Detail Panel
Full Detail View
// Shift detail panel component
const ShiftDetailPanel = ({ shift, onClose }) => (
<div className="card-glass p-6 space-y-6">
{/* Header */}
<div className="flex justify-between items-start">
<div>
<h2 className="text-xl font-semibold">Shift Details</h2>
<p className="text-secondary-text">
{moment.tz(shift.start_time, UK_TIMEZONE).format('dddd, D MMMM YYYY')}
</p>
</div>
<ShiftStatusBadge status={shift.status} />
</div>
{/* Time section */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm text-secondary-text">Start Time</label>
<div className="text-lg font-medium">
{formatTime(shift.start_time)}
</div>
</div>
<div>
<label className="text-sm text-secondary-text">End Time</label>
<div className="text-lg font-medium">
{formatTime(shift.end_time)}
</div>
</div>
</div>
{/* Duration */}
<div>
<label className="text-sm text-secondary-text">Duration</label>
<div className="text-lg font-medium">
{calculateDuration(shift.start_time, shift.end_time)}
</div>
</div>
{/* Room */}
<div>
<label className="text-sm text-secondary-text">Room</label>
<RoomDisplay room={shift.room} />
</div>
{/* Type */}
<div>
<label className="text-sm text-secondary-text">Shift Type</label>
<ShiftTypeBadge type={shift.shift_type} />
</div>
{/* Notes */}
<ShiftNotes notes={shift.notes} />
{/* Actions */}
<div className="flex gap-3 pt-4 border-t border-white/10">
<button
onClick={() => requestSwap(shift)}
className="form-button secondary flex-1"
>
Request Swap
</button>
<button
onClick={onClose}
className="form-button flex-1"
>
Close
</button>
</div>
</div>
)
Related Shifts
Same Day Shifts
View other shifts on the same day:
// Fetch same-day shifts
const getSameDayShifts = async (shiftDate: string, companyId: string) => {
const startOfDay = moment(shiftDate).startOf('day').toISOString()
const endOfDay = moment(shiftDate).endOf('day').toISOString()
const { data } = await supabase
.from('shifts')
.select('*, staff:staff(*), room:rooms(*)')
.eq('company_id', companyId)
.gte('start_time', startOfDay)
.lte('start_time', endOfDay)
.eq('status', 'confirmed')
return data
}
Coverage Information
| Metric | Description |
|---|---|
| Total Staff | Staff working that day |
| Same Room | Colleagues in same room |
| Overlap | Shifts overlapping yours |
Shift History
Viewing Changes
Track changes to a shift:
// Shift audit information
interface ShiftAudit {
id: string
shift_id: string
action: 'created' | 'updated' | 'cancelled'
changed_by: string
changed_at: string
changes: {
field: string
old_value: string | null
new_value: string
}[]
}
Change Types
| Change | Description |
|---|---|
| Time Changed | Start or end time modified |
| Room Changed | Different room assigned |
| Status Changed | Draft → Confirmed, etc. |
| Notes Updated | Instructions modified |
Print/Export
Print Shift Details
// Generate printable shift details
const printShiftDetails = (shift: Shift) => {
const printWindow = window.open('', '_blank')
printWindow.document.write(`
<html>
<head><title>Shift Details</title></head>
<body>
<h1>Shift Details</h1>
<p><strong>Date:</strong> ${formatDate(shift.start_time)}</p>
<p><strong>Time:</strong> ${formatTime(shift.start_time)} - ${formatTime(shift.end_time)}</p>
<p><strong>Room:</strong> ${shift.room?.name || 'TBC'}</p>
<p><strong>Type:</strong> ${shift.shift_type}</p>
${shift.notes ? `<p><strong>Notes:</strong> ${shift.notes}</p>` : ''}
</body>
</html>
`)
printWindow.print()
}
Add to Calendar
// Generate calendar event
const addToCalendar = (shift: Shift) => {
const event = {
title: `Work: ${shift.room?.name || 'Shift'}`,
start: shift.start_time,
end: shift.end_time,
location: shift.room?.name,
description: shift.notes
}
// Generate Google Calendar URL
const googleCalUrl = `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${encodeURIComponent(event.title)}&dates=${formatGoogleDate(event.start)}/${formatGoogleDate(event.end)}&details=${encodeURIComponent(event.description || '')}&location=${encodeURIComponent(event.location || '')}`
window.open(googleCalUrl, '_blank')
}
Mobile View
Responsive Details
On mobile devices:
// Mobile shift detail view
const MobileShiftDetail = ({ shift }) => (
<div className="fixed inset-0 bg-background z-50 overflow-y-auto">
<div className="p-4 space-y-4">
{/* Header with close button */}
<div className="flex justify-between items-center">
<h2 className="text-lg font-semibold">Shift Details</h2>
<button className="p-2" onClick={onClose}>
<X className="w-6 h-6" />
</button>
</div>
{/* Date */}
<div className="text-center py-4 border-b border-white/10">
<div className="text-2xl font-bold">
{moment.tz(shift.start_time, UK_TIMEZONE).format('ddd D MMM')}
</div>
<ShiftStatusBadge status={shift.status} />
</div>
{/* Details list */}
<div className="space-y-4">
<DetailRow label="Time" value={`${formatTime(shift.start_time)} - ${formatTime(shift.end_time)}`} />
<DetailRow label="Duration" value={calculateDuration(shift)} />
<DetailRow label="Room" value={shift.room?.name || 'TBC'} />
<DetailRow label="Type" value={formatShiftType(shift.shift_type)} />
</div>
{/* Notes */}
{shift.notes && (
<div className="p-4 bg-white/5 rounded-lg">
<h3 className="font-medium mb-2">Notes</h3>
<p className="text-secondary-text">{shift.notes}</p>
</div>
)}
{/* Actions */}
<div className="fixed bottom-0 left-0 right-0 p-4 bg-background border-t border-white/10">
<button className="form-button w-full">
Request Swap
</button>
</div>
</div>
</div>
)
Best Practices
For Understanding Shifts
- Check Room Assignment - Know where to report
- Note the Times - Be on time
- Read Notes - Follow special instructions
- Verify Status - Ensure shift is confirmed
- Plan Transport - Allow travel time
Shift Preparation
| Action | When |
|---|---|
| Check details | Day before |
| Confirm room | Morning of shift |
| Review notes | Before arriving |
| Check updates | If notified |
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Details not loading | Network error | Refresh page |
| Wrong time showing | Timezone issue | Verify UK time |
| Room missing | Not yet assigned | Contact manager |
| Status unclear | Pending confirmation | Await update |
Questions to Ask
If details are unclear:
- What room should I report to?
- What time exactly should I start?
- Are there any special instructions?
- Who should I contact if there's a problem?
Related Documentation
- Viewing Shifts - Schedule overview
- Shift Trading - Trade with colleagues
- Clock In/Out - Start your shift
- Dashboard - Staff dashboard
Source Files:
src/app/api/staff/shifts/[shiftId]/route.ts- Single shift APIsrc/components/staff/ShiftDetailPanel.tsx- Detail panel componentsrc/types/shift.types.ts- Type definitionssrc/lib/utils/timezone.ts- Time formatting