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