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