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:
- Notification Settings - What alerts you receive
- Display Settings - How information appears
- Calendar Preferences - Default calendar views
- Communication Preferences - How you're contacted
Accessing Preferences
- Navigate to My Profile in Staff Portal
- Click Preferences or Settings tab
- Adjust settings as needed
- Save changes
Notification Preferences
Notification Types
| Type | Description | Default |
|---|---|---|
| Shift Reminders | Upcoming shift alerts | ✅ On |
| Schedule Changes | Shift modifications | ✅ On |
| Leave Updates | Request status changes | ✅ On |
| Time Tracking | Clock in/out reminders | ✅ On |
| Announcements | Company notices | ✅ On |
| Swap Requests | Shift swap notifications | ✅ On |
Notification Channels
| Channel | Description | Control |
|---|---|---|
| In-App | Notifications within Shyfts | Per-type toggle |
| Email notifications | Per-type toggle | |
| Push | Browser push notifications | Master 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
| Option | Description | Best For |
|---|---|---|
| 1 hour before | Short notice | Regular staff |
| 2 hours before | Some prep time | Travelling staff |
| 24 hours before | Day before | Planning ahead |
| 48 hours before | Two days notice | Long 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
| Change | Notified |
|---|---|
| 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
| Status | Notification |
|---|---|
| Request Submitted | Confirmation sent |
| Request Approved | Approval notification |
| Request Rejected | Rejection with reason |
| Request Cancelled | Cancellation 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
| Reminder | Timing | Purpose |
|---|---|---|
| Clock In | Shift start | Don't forget to clock in |
| Break Reminder | After 4 hours | Take required break |
| Clock Out | Shift end | Remember 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
| Setting | Options | Default |
|---|---|---|
| Theme | System, Light, Dark | System |
| Accent Colour | Coral (default) | Coral |
| Font Size | Small, Medium, Large | Medium |
| Compact Mode | On/Off | Off |
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
| View | Description | Best For |
|---|---|---|
| Day | Single day view | Detailed planning |
| Week | 7-day view | Weekly overview |
| Month | Monthly calendar | Long-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
| Setting | Description | Default |
|---|---|---|
| Notification Emails | Receive email alerts | ✅ On |
| Daily Digest | Summary of daily activities | ❌ Off |
| Weekly Summary | Weekly 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
| Setting | Description | Mobile Default |
|---|---|---|
| Push Notifications | Device push alerts | Prompt on first use |
| Vibration | Haptic feedback | ✅ On |
| Quick Actions | Home screen shortcuts | Available |
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
| Setting | Description | Default |
|---|---|---|
| Reduce Motion | Minimise animations | ❌ Off |
| High Contrast | Increase colour contrast | ❌ Off |
| Screen Reader | Optimise for screen readers | Auto-detect |
| Keyboard Navigation | Enhanced 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
| Issue | Cause | Solution |
|---|---|---|
| Settings not saving | Network error | Retry, check connection |
| Notifications not received | Browser permissions | Enable in browser settings |
| Theme not applying | Cache issue | Clear browser cache |
| Push not working | Permission denied | Re-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
Recommended Settings
| User Type | Shift Reminders | Schedule Changes | Time Tracking |
|---|---|---|---|
| Office Staff | 24h before | Clock in only | |
| Shift Workers | 2h before | Push | All reminders |
| Managers | Off | Email + Push | Off |
Tips
- Start with defaults - Adjust as needed
- Enable push - For immediate alerts
- Use email - For important updates
- Review periodically - Update as needs change
Related Documentation
- Updating Profile - Edit your profile
- Changing Password - Password security
- Dashboard - Staff dashboard
- Mobile App - Mobile guide
Source Files:
src/app/api/staff/preferences/route.ts- Preferences APIsrc/components/staff/PreferencesForm.tsx- Settings formsrc/contexts/PreferencesContext.tsx- Preferences statesrc/lib/notifications/preferences.ts- Notification utilities