Skip to main content

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:

  1. Timing - Start and end times with duration
  2. Location - Room or facility assignment
  3. Type - Shift category (Standard, Night, etc.)
  4. Status - Confirmation status
  5. Notes - Special instructions or information

Accessing Shift Details

  1. Navigate to My Schedule in Staff Portal
  2. Click on any shift in the calendar
  3. The shift details panel opens

Shift Information

Core Details

FieldDescriptionExample
DateShift date14/01/2025
Start TimeWhen shift begins09:00
End TimeWhen shift ends17:30
DurationTotal shift length8h 30m
RoomAssigned locationConsultation Room 1
TypeShift categoryStandard
StatusConfirmation stateConfirmed

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

StartEndDuration
09:0017:008h
09:0017:308h 30m
08:0020:0012h
22:0006: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

ElementDescription
NameRoom identifier
TypeRoom category
LocationFloor/building info
CapacityMaximum 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

TypeDescriptionTypical Hours
StandardRegular day shift09:00 - 17:30
MorningEarly shift06:00 - 14:00
EveningLate shift14:00 - 22:00
NightOvernight shift22:00 - 06:00
On-CallStandby dutyVariable
TrainingTraining/developmentVariable

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

StatusMeaningAction Required
ConfirmedShift is scheduledReport for duty
DraftTentative scheduleAwait confirmation
CancelledShift was removedDo 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

TypeExample
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>
)

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

MetricDescription
Total StaffStaff working that day
Same RoomColleagues in same room
OverlapShifts 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

ChangeDescription
Time ChangedStart or end time modified
Room ChangedDifferent room assigned
Status ChangedDraft → Confirmed, etc.
Notes UpdatedInstructions modified

Print/Export

// 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

  1. Check Room Assignment - Know where to report
  2. Note the Times - Be on time
  3. Read Notes - Follow special instructions
  4. Verify Status - Ensure shift is confirmed
  5. Plan Transport - Allow travel time

Shift Preparation

ActionWhen
Check detailsDay before
Confirm roomMorning of shift
Review notesBefore arriving
Check updatesIf notified

Troubleshooting

Common Issues

IssueCauseSolution
Details not loadingNetwork errorRefresh page
Wrong time showingTimezone issueVerify UK time
Room missingNot yet assignedContact manager
Status unclearPending confirmationAwait update

Questions to Ask

If details are unclear:

  1. What room should I report to?
  2. What time exactly should I start?
  3. Are there any special instructions?
  4. Who should I contact if there's a problem?


Source Files:

  • src/app/api/staff/shifts/[shiftId]/route.ts - Single shift API
  • src/components/staff/ShiftDetailPanel.tsx - Detail panel component
  • src/types/shift.types.ts - Type definitions
  • src/lib/utils/timezone.ts - Time formatting