Skip to main content

Preferences

This guide covers how to configure your notification preferences, display settings, and personalise your Shyfts experience.


Overview

What are Preferences?

Preferences control how you receive information and interact with Shyfts:

  1. Notification Settings - What alerts you receive
  2. Display Settings - How information appears
  3. Calendar Preferences - Default calendar views
  4. Communication Preferences - How you're contacted

Accessing Preferences

  1. Navigate to My Profile in Staff Portal
  2. Click Preferences or Settings tab
  3. Adjust settings as needed
  4. Save changes

Notification Preferences

Notification Types

TypeDescriptionDefault
Shift RemindersUpcoming shift alerts✅ On
Schedule ChangesShift modifications✅ On
Leave UpdatesRequest status changes✅ On
Time TrackingClock in/out reminders✅ On
AnnouncementsCompany notices✅ On
Swap RequestsShift swap notifications✅ On

Notification Channels

ChannelDescriptionControl
In-AppNotifications within ShyftsPer-type toggle
EmailEmail notificationsPer-type toggle
PushBrowser push notificationsMaster toggle

Setting Notification Preferences

// Notification preferences structure
interface NotificationPreferences {
// Shift notifications
shiftReminders: {
enabled: boolean
timing: '1h' | '2h' | '24h' | '48h' // How far in advance
channels: ('in_app' | 'email' | 'push')[]
}

// Schedule changes
scheduleChanges: {
enabled: boolean
channels: ('in_app' | 'email')[]
}

// Leave updates
leaveUpdates: {
enabled: boolean
channels: ('in_app' | 'email')[]
}

// Time tracking
timeTracking: {
clockInReminder: boolean
clockOutReminder: boolean
breakReminder: boolean
channels: ('in_app' | 'push')[]
}

// Swap requests
swapRequests: {
newRequests: boolean
statusUpdates: boolean
channels: ('in_app' | 'email')[]
}

// Company announcements
announcements: {
enabled: boolean
channels: ('in_app' | 'email')[]
}
}

Shift Reminders

Reminder Timing

OptionDescriptionBest For
1 hour beforeShort noticeRegular staff
2 hours beforeSome prep timeTravelling staff
24 hours beforeDay beforePlanning ahead
48 hours beforeTwo days noticeLong commutes

Reminder Settings

// Configure shift reminder preferences
const shiftReminderSettings = {
enabled: true,
timing: '2h',
channels: ['in_app', 'push'],
includeDetails: {
room: true,
time: true,
notes: true
}
}

Example Reminder

// Shift reminder notification
{
type: "shift_reminder",
title: "Upcoming Shift",
message: "Your shift starts in 2 hours",
data: {
shiftId: "shift-uuid",
startTime: "2025-01-15T09:00:00Z",
room: "Consultation Room 1",
duration: "8h 30m"
},
created_at: "2025-01-15T07:00:00Z"
}

Schedule Change Notifications

What Triggers Notifications

ChangeNotified
Shift time changed✅ Yes
Shift room changed✅ Yes
Shift cancelled✅ Yes
New shift added✅ Yes
Shift notes updated⚡ Optional

Change Notification Settings

// Schedule change notification preferences
const scheduleChangeSettings = {
enabled: true,
notifyFor: {
timeChanges: true,
roomChanges: true,
cancellations: true,
newShifts: true,
noteUpdates: false
},
channels: ['in_app', 'email']
}

Example Change Notification

// Schedule change notification
{
type: "schedule_change",
title: "Shift Time Changed",
message: "Your shift on 15/01 has been updated",
data: {
shiftId: "shift-uuid",
changeType: "time_change",
oldStart: "2025-01-15T09:00:00Z",
newStart: "2025-01-15T10:00:00Z",
changedBy: "Manager Name"
}
}

Leave Notifications

Leave Status Updates

StatusNotification
Request SubmittedConfirmation sent
Request ApprovedApproval notification
Request RejectedRejection with reason
Request CancelledCancellation confirmed

Leave Notification Settings

// Leave notification preferences
const leaveNotificationSettings = {
enabled: true,
notifyFor: {
submitted: true,
approved: true,
rejected: true,
cancelled: true,
balanceUpdates: true
},
channels: ['in_app', 'email']
}

Time Tracking Notifications

Clock In/Out Reminders

ReminderTimingPurpose
Clock InShift startDon't forget to clock in
Break ReminderAfter 4 hoursTake required break
Clock OutShift endRemember to clock out

Time Tracking Settings

// Time tracking notification preferences
const timeTrackingSettings = {
clockInReminder: {
enabled: true,
timing: '5m', // 5 minutes before shift
channel: 'push'
},
breakReminder: {
enabled: true,
afterHours: 4,
channel: 'in_app'
},
clockOutReminder: {
enabled: true,
timing: '5m', // 5 minutes after shift ends
channel: 'push'
}
}

Display Settings

Theme Preferences

SettingOptionsDefault
ThemeSystem, Light, DarkSystem
Accent ColourCoral (default)Coral
Font SizeSmall, Medium, LargeMedium
Compact ModeOn/OffOff

Theme Configuration

// Display theme preferences
const themeSettings = {
theme: 'system', // 'light' | 'dark' | 'system'
accentColour: 'coral',
fontSize: 'medium', // 'small' | 'medium' | 'large'
compactMode: false,
reduceMotion: false,
highContrast: false
}

Applying Theme

// Apply theme settings
const applyTheme = (settings: ThemeSettings) => {
const root = document.documentElement

// Set theme
if (settings.theme === 'system') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
root.classList.toggle('dark', prefersDark)
} else {
root.classList.toggle('dark', settings.theme === 'dark')
}

// Set font size
root.style.setProperty('--base-font-size', fontSizes[settings.fontSize])

// Set compact mode
root.classList.toggle('compact', settings.compactMode)
}

Calendar Preferences

Default View

ViewDescriptionBest For
DaySingle day viewDetailed planning
Week7-day viewWeekly overview
MonthMonthly calendarLong-term view

Calendar Settings

// Calendar display preferences
const calendarSettings = {
defaultView: 'week', // 'day' | 'week' | 'month'
weekStartsOn: 'monday', // UK default
showWeekNumbers: false,
timeFormat: '24h', // UK default
dateFormat: 'DD/MM/YYYY', // UK default
showPastShifts: true,
colourByRoom: true, // Colour shifts by room
colourByRole: false // Alternative: colour by role
}

View Preferences

// Calendar view configuration
const viewPreferences = {
// Day view
dayView: {
hourStart: 6, // 06:00
hourEnd: 22, // 22:00
slotDuration: 30 // 30-minute slots
},

// Week view
weekView: {
showWeekends: true,
dayHeaderFormat: 'ddd D' // "Mon 15"
},

// Month view
monthView: {
showDayNumbers: true,
compactShifts: true
}
}

Communication Preferences

Email Preferences

SettingDescriptionDefault
Notification EmailsReceive email alerts✅ On
Daily DigestSummary of daily activities❌ Off
Weekly SummaryWeekly schedule overview❌ Off

Email Settings

// Email communication preferences
const emailSettings = {
notificationEmails: true,
dailyDigest: {
enabled: false,
time: '07:00', // Send at 7am
include: ['shifts', 'tasks', 'notifications']
},
weeklySummary: {
enabled: false,
day: 'sunday', // Send Sunday evening
time: '18:00'
},
marketingEmails: false // Product updates
}

Preferences Form

Settings Component

// Preferences form component
const PreferencesForm = ({ preferences, onSave }) => {
const [settings, setSettings] = useState(preferences)
const [saving, setSaving] = useState(false)

const handleSave = async () => {
setSaving(true)
try {
await onSave(settings)
// Show success message
} catch (error) {
// Show error message
} finally {
setSaving(false)
}
}

return (
<div className="space-y-6">
{/* Notification Section */}
<Section title="Notifications">
<Toggle
label="Shift Reminders"
description="Get notified before your shifts"
checked={settings.notifications.shiftReminders}
onChange={(checked) => updateSetting('notifications.shiftReminders', checked)}
/>

<Select
label="Reminder Timing"
value={settings.notifications.reminderTiming}
options={[
{ value: '1h', label: '1 hour before' },
{ value: '2h', label: '2 hours before' },
{ value: '24h', label: '24 hours before' }
]}
onChange={(value) => updateSetting('notifications.reminderTiming', value)}
/>

<Toggle
label="Schedule Changes"
description="Get notified when your schedule changes"
checked={settings.notifications.scheduleChanges}
onChange={(checked) => updateSetting('notifications.scheduleChanges', checked)}
/>

<Toggle
label="Leave Updates"
description="Get notified about leave request status"
checked={settings.notifications.leaveUpdates}
onChange={(checked) => updateSetting('notifications.leaveUpdates', checked)}
/>
</Section>

{/* Display Section */}
<Section title="Display">
<Select
label="Theme"
value={settings.display.theme}
options={[
{ value: 'system', label: 'System Default' },
{ value: 'light', label: 'Light' },
{ value: 'dark', label: 'Dark' }
]}
onChange={(value) => updateSetting('display.theme', value)}
/>

<Select
label="Font Size"
value={settings.display.fontSize}
options={[
{ value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' }
]}
onChange={(value) => updateSetting('display.fontSize', value)}
/>

<Toggle
label="Compact Mode"
description="Show more content in less space"
checked={settings.display.compactMode}
onChange={(checked) => updateSetting('display.compactMode', checked)}
/>
</Section>

{/* Calendar Section */}
<Section title="Calendar">
<Select
label="Default View"
value={settings.calendar.defaultView}
options={[
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week' },
{ value: 'month', label: 'Month' }
]}
onChange={(value) => updateSetting('calendar.defaultView', value)}
/>

<Toggle
label="Colour by Room"
description="Colour-code shifts by assigned room"
checked={settings.calendar.colourByRoom}
onChange={(checked) => updateSetting('calendar.colourByRoom', checked)}
/>
</Section>

{/* Save Button */}
<button
onClick={handleSave}
disabled={saving}
className="form-button w-full"
>
{saving ? 'Saving...' : 'Save Preferences'}
</button>
</div>
)
}

Toggle Component

Preference Toggle

// Toggle switch component
const Toggle = ({ label, description, checked, onChange }) => (
<div className="flex items-center justify-between py-3 border-b border-white/10">
<div>
<div className="font-medium">{label}</div>
{description && (
<div className="text-sm text-secondary-text">{description}</div>
)}
</div>
<button
role="switch"
aria-checked={checked}
onClick={() => onChange(!checked)}
className={`
relative w-12 h-6 rounded-full transition-colors
${checked ? 'bg-coral-500' : 'bg-white/20'}
`}
>
<span
className={`
absolute top-1 w-4 h-4 rounded-full bg-white transition-transform
${checked ? 'left-7' : 'left-1'}
`}
/>
</button>
</div>
)

Saving Preferences

API Endpoint

Endpoint: PATCH /api/staff/preferences

// Save preferences API
const savePreferences = async (preferences: UserPreferences) => {
const response = await fetch('/api/staff/preferences', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(preferences)
})

return response.json()
}

Local Storage Backup

// Cache preferences locally
const cachePreferences = (preferences: UserPreferences) => {
localStorage.setItem('cflow_preferences', JSON.stringify(preferences))
}

// Load cached preferences
const loadCachedPreferences = (): UserPreferences | null => {
const cached = localStorage.getItem('cflow_preferences')
return cached ? JSON.parse(cached) : null
}

Default Preferences

System Defaults

// Default preferences for new users
const defaultPreferences: UserPreferences = {
notifications: {
shiftReminders: {
enabled: true,
timing: '2h',
channels: ['in_app', 'push']
},
scheduleChanges: {
enabled: true,
channels: ['in_app', 'email']
},
leaveUpdates: {
enabled: true,
channels: ['in_app', 'email']
},
timeTracking: {
clockInReminder: true,
clockOutReminder: true,
breakReminder: true,
channels: ['push']
},
swapRequests: {
newRequests: true,
statusUpdates: true,
channels: ['in_app', 'email']
},
announcements: {
enabled: true,
channels: ['in_app']
}
},
display: {
theme: 'system',
fontSize: 'medium',
compactMode: false,
reduceMotion: false
},
calendar: {
defaultView: 'week',
weekStartsOn: 'monday',
timeFormat: '24h',
dateFormat: 'DD/MM/YYYY',
colourByRoom: true
},
email: {
notificationEmails: true,
dailyDigest: false,
weeklySummary: false
}
}

Reset to Defaults

// Reset all preferences to defaults
const resetPreferences = async () => {
const confirmed = await showConfirmation(
'Reset all preferences to default settings?'
)

if (confirmed) {
await savePreferences(defaultPreferences)
applyPreferences(defaultPreferences)
}
}

Mobile Preferences

Mobile-Specific Settings

SettingDescriptionMobile Default
Push NotificationsDevice push alertsPrompt on first use
VibrationHaptic feedback✅ On
Quick ActionsHome screen shortcutsAvailable

Mobile Form

// Mobile preferences form
const MobilePreferencesForm = ({ preferences, onSave }) => (
<div className="p-4 space-y-4">
<h2 className="text-lg font-semibold">Preferences</h2>

{/* Notification toggles */}
<div className="space-y-3">
<MobileToggle
icon={Bell}
label="Shift Reminders"
checked={preferences.shiftReminders}
onChange={(v) => updatePref('shiftReminders', v)}
/>

<MobileToggle
icon={Calendar}
label="Schedule Changes"
checked={preferences.scheduleChanges}
onChange={(v) => updatePref('scheduleChanges', v)}
/>

<MobileToggle
icon={Clock}
label="Clock Reminders"
checked={preferences.clockReminders}
onChange={(v) => updatePref('clockReminders', v)}
/>
</div>

{/* Theme selector */}
<div className="pt-4 border-t border-white/10">
<label className="block text-sm font-medium mb-2">Theme</label>
<div className="grid grid-cols-3 gap-2">
<ThemeButton value="system" current={preferences.theme} />
<ThemeButton value="light" current={preferences.theme} />
<ThemeButton value="dark" current={preferences.theme} />
</div>
</div>

{/* Save button */}
<button className="form-button w-full py-3 mt-4">
Save Preferences
</button>
</div>
)

Accessibility Preferences

Accessibility Settings

SettingDescriptionDefault
Reduce MotionMinimise animations❌ Off
High ContrastIncrease colour contrast❌ Off
Screen ReaderOptimise for screen readersAuto-detect
Keyboard NavigationEnhanced keyboard support✅ On

Accessibility Configuration

// Accessibility preferences
const accessibilitySettings = {
reduceMotion: false,
highContrast: false,
screenReaderOptimised: false,
keyboardNavigation: true,
focusIndicators: 'visible', // 'visible' | 'on-keyboard'
textSpacing: 'normal' // 'normal' | 'increased'
}

// Apply accessibility settings
const applyAccessibility = (settings: AccessibilitySettings) => {
const root = document.documentElement

root.classList.toggle('reduce-motion', settings.reduceMotion)
root.classList.toggle('high-contrast', settings.highContrast)
root.classList.toggle('increased-spacing', settings.textSpacing === 'increased')
}

Troubleshooting

Common Issues

IssueCauseSolution
Settings not savingNetwork errorRetry, check connection
Notifications not receivedBrowser permissionsEnable in browser settings
Theme not applyingCache issueClear browser cache
Push not workingPermission deniedRe-enable in browser

Push Notification Troubleshooting

// Check push notification status
const checkPushStatus = async () => {
// Check if supported
if (!('Notification' in window)) {
return { supported: false, reason: 'Browser does not support notifications' }
}

// Check permission
if (Notification.permission === 'denied') {
return { supported: true, enabled: false, reason: 'Permission denied' }
}

if (Notification.permission === 'default') {
return { supported: true, enabled: false, reason: 'Permission not requested' }
}

return { supported: true, enabled: true }
}

Best Practices

User TypeShift RemindersSchedule ChangesTime Tracking
Office Staff24h beforeEmailClock in only
Shift Workers2h beforePushAll reminders
ManagersOffEmail + PushOff

Tips

  1. Start with defaults - Adjust as needed
  2. Enable push - For immediate alerts
  3. Use email - For important updates
  4. Review periodically - Update as needs change


Source Files:

  • src/app/api/staff/preferences/route.ts - Preferences API
  • src/components/staff/PreferencesForm.tsx - Settings form
  • src/contexts/PreferencesContext.tsx - Preferences state
  • src/lib/notifications/preferences.ts - Notification utilities