Updating Profile
This guide covers how to view your profile information and update the fields you're permitted to change.
Overview
What is Your Profile?
Your profile contains your personal and employment information:
- Personal Information - Name, email, phone, address
- Employment Details - Start date, contract type, hours
- Role Information - Job role and permissions
- Company Details - Your company assignment
What You Can Edit
Staff members can edit limited fields to maintain data integrity:
| Category | Editable | Read-Only |
|---|---|---|
| Personal | Phone, Address | Name, Email, Date of Birth |
| Employment | None | Start Date, End Date, Contract Hours |
| Role | None | Role Name, Permissions |
| Company | None | Company Name |
Why Limited Editing?
Employment and role information is managed by your Company Manager to ensure payroll accuracy and compliance with HR policies. Contact your manager to request changes to read-only fields.
Viewing Your Profile
Accessing Profile
- Navigate to My Profile in Staff Portal
- Or click your name in the navigation header
- Profile details are displayed
Profile API
Endpoint: GET /api/staff/profile
// Source: src/app/api/staff/profile/route.ts:22-119
// Fetch your profile data
const getProfile = async () => {
const response = await fetch('/api/staff/profile')
const { data } = await response.json()
return data.profile
}
Profile Data Structure
// Profile response structure
interface ProfileData {
// Identity
id: string
companyId: string
email: string
firstName: string
lastName: string
fullName: string
// Contact
phone: string | null
address: string | null
// Employment
dateOfBirth: string | null
employmentStartDate: string | null
employmentEndDate: string | null
employmentType: string | null
contractHours: number | null
hourlyRate: number | null
// Status
isActive: boolean
roleId: string | null
roleName: string | null
roleColor: string | null
notes: string | null
// Relations
company: {
id: string
name: string
industry_template_id: string
}
role: {
id: string
name: string
description: string
color: string
permissions: object
}
// Edit permissions
canEdit: {
personalInfo: boolean // Phone and address only
emergency: boolean // Not available
preferences: boolean // Not available
avatar: boolean // Not available
bio: boolean // Not available
employment: boolean // Read-only
company: boolean // Read-only
}
}
Profile Sections
Personal Information
| Field | Description | Editable |
|---|---|---|
| Full Name | First and last name | ❌ Read-only |
| Login email address | ❌ Read-only | |
| Phone | Contact phone number | ✅ Editable |
| Address | Home address | ✅ Editable |
| Date of Birth | Your birth date | ❌ Read-only |
Employment Details
| Field | Description | Format |
|---|---|---|
| Start Date | Employment commencement | DD/MM/YYYY |
| End Date | Employment end (if applicable) | DD/MM/YYYY |
| Employment Type | Full-time, part-time, casual | Text |
| Contract Hours | Weekly contracted hours | Number |
| Hourly Rate | Pay rate (if hourly) | £XX.XX |
Role Information
| Field | Description |
|---|---|
| Role Name | Your assigned job role |
| Role Description | What the role entails |
| Role Colour | Visual identifier on schedules |
| Permissions | What you can access |
Company Information
| Field | Description |
|---|---|
| Company Name | Your employer |
| Industry | Business type |
Updating Your Profile
Editable Fields
You can update:
| Field | Validation | Example |
|---|---|---|
| Phone | Valid format | +44 7700 900000 |
| Address | Free text | 123 High Street, London |
Update API
Endpoint: PATCH /api/staff/profile
// Source: src/app/api/staff/profile/route.ts:125-254
// Update your profile
const updateProfile = async (updates: ProfileUpdate) => {
const response = await fetch('/api/staff/profile', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
})
return response.json()
}
// Example usage
await updateProfile({
phone: '+44 7700 900123',
address: '456 New Street, Manchester, M1 1AA'
})
Allowed Fields
// Source: src/app/api/staff/profile/route.ts:154-158
// Only these fields can be updated by staff
const allowedFields = {
phone: body.phone,
address: body.address
}
Update Response
// Successful update response
{
success: true,
data: {
profile: {
id: "staff-uuid",
firstName: "John",
lastName: "Smith",
phone: "+44 7700 900123",
address: "456 New Street, Manchester, M1 1AA",
// ... all profile fields
}
}
}
Phone Number
Format Guidelines
| Format | Example | Status |
|---|---|---|
| UK Mobile | 07700 900123 | ✅ Valid |
| UK Mobile (+44) | +44 7700 900123 | ✅ Valid |
| UK Landline | 020 7946 0958 | ✅ Valid |
| International | +1 555 123 4567 | ✅ Valid |
Phone Validation
// Phone number validation
const isValidPhone = (phone: string): boolean => {
// Remove spaces and special characters for validation
const cleaned = phone.replace(/[\s\-\(\)]/g, '')
// Check for valid patterns
const patterns = [
/^(\+44|0)7\d{9}$/, // UK mobile
/^(\+44|0)\d{10}$/, // UK landline
/^\+\d{10,15}$/ // International
]
return patterns.some(p => p.test(cleaned))
}
Update Phone Example
// Update phone number
const updatePhone = async (newPhone: string) => {
const response = await fetch('/api/staff/profile', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: newPhone
})
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message)
}
return response.json()
}
Address
Address Format
Provide a complete postal address:
Line 1: Street address
Line 2: Additional info (optional)
City: City or town
Postcode: UK postcode
Example Addresses
// Single-line address
"123 High Street, London, SW1A 1AA"
// Multi-line address
"Flat 4B, Wellington House
15 Market Square
Manchester
M1 2AB"
Update Address Example
// Update address
const updateAddress = async (newAddress: string) => {
const response = await fetch('/api/staff/profile', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: newAddress
})
})
return response.json()
}
// Usage
await updateAddress('456 New Road, Birmingham, B1 1BB')
Profile Display Component
Profile View
// Profile display component
const ProfileView = ({ profile }: { profile: ProfileData }) => (
<div className="card-glass p-6 space-y-6">
{/* Header */}
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-full bg-coral-gradient flex items-center justify-center text-white text-xl font-bold">
{profile.firstName[0]}{profile.lastName[0]}
</div>
<div>
<h2 className="text-xl font-semibold">{profile.fullName}</h2>
<p className="text-secondary-text">{profile.roleName}</p>
</div>
</div>
{/* Contact Section */}
<Section title="Contact Information">
<Field label="Email" value={profile.email} readonly />
<Field label="Phone" value={profile.phone} editable />
<Field label="Address" value={profile.address} editable />
</Section>
{/* Employment Section */}
<Section title="Employment Details">
<Field label="Start Date" value={formatDate(profile.employmentStartDate)} readonly />
<Field label="Contract Type" value={profile.employmentType} readonly />
<Field label="Contract Hours" value={`${profile.contractHours}h/week`} readonly />
</Section>
{/* Company Section */}
<Section title="Company">
<Field label="Company" value={profile.company.name} readonly />
<Field label="Role" value={profile.roleName} readonly />
</Section>
</div>
)
Edit Form
// Profile edit form
const ProfileEditForm = ({ profile, onSave }) => {
const [phone, setPhone] = useState(profile.phone || '')
const [address, setAddress] = useState(profile.address || '')
const [saving, setSaving] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setSaving(true)
try {
await onSave({ phone, address })
// Show success message
} catch (error) {
// Show error message
} finally {
setSaving(false)
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1">Phone Number</label>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="form-input w-full"
placeholder="+44 7700 900123"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">Address</label>
<textarea
value={address}
onChange={(e) => setAddress(e.target.value)}
className="form-input w-full"
rows={3}
placeholder="Enter your full address"
/>
</div>
<button
type="submit"
disabled={saving}
className="form-button w-full"
>
{saving ? 'Saving...' : 'Save Changes'}
</button>
</form>
)
}
Validation
Phone Validation
// Source: src/lib/validation/staff.schemas.ts (similar pattern)
const phoneSchema = z.string()
.optional()
.refine(
(val) => !val || /^[\+\d\s\-\(\)]{10,20}$/.test(val),
'Invalid phone number format'
)
Address Validation
// Address validation
const addressSchema = z.string()
.optional()
.max(500, 'Address too long (max 500 characters)')
Validation Errors
| Error | Cause | Solution |
|---|---|---|
| Invalid phone format | Incorrect number format | Use valid UK or international format |
| Address too long | Exceeds 500 characters | Shorten address |
| Validation failed | Multiple issues | Check all field formats |
Read-Only Information
Requesting Changes
For read-only fields, contact your Company Manager:
| Change Required | Who to Contact | Method |
|---|---|---|
| Name correction | Company Manager | Email or in person |
| Email change | Company Manager | Formal request |
| Role change | Company Manager | HR process |
| Contract hours | Company Manager | Contract amendment |
| Employment dates | Company Manager | HR records |
Why Fields Are Read-Only
| Field | Reason |
|---|---|
| Name | Legal documents, payroll |
| Authentication, communications | |
| Employment Dates | Entitlements calculation |
| Contract Hours | Payroll, scheduling |
| Role | Permissions, access control |
Best Practices
Keeping Information Current
- Update Phone - Keep contact number current
- Update Address - Ensure postal address is correct
- Report Errors - Notify manager of incorrect data
- Review Regularly - Check profile quarterly
Privacy Considerations
| Data | Visibility |
|---|---|
| Your Email | Company Manager, System Admin |
| Your Phone | Company Manager only |
| Your Address | Company Manager only |
| Your Role | All company staff |
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Cannot save changes | Network error | Retry, check connection |
| Phone rejected | Invalid format | Use valid format |
| Address too long | Exceeds limit | Shorten address |
| Profile not loading | Session expired | Re-login |
Error Messages
| Error | Meaning | Action |
|---|---|---|
| Authentication required | Session expired | Login again |
| Validation failed | Invalid data | Check field formats |
| Staff record not found | Account issue | Contact manager |
| Forbidden | Account inactive | Contact manager |
Mobile View
Responsive Profile
On mobile devices:
// Mobile profile display
const MobileProfile = ({ profile }) => (
<div className="p-4 space-y-4">
{/* Avatar and name */}
<div className="text-center">
<div className="w-20 h-20 mx-auto rounded-full bg-coral-gradient flex items-center justify-center text-white text-2xl font-bold">
{profile.firstName[0]}{profile.lastName[0]}
</div>
<h2 className="mt-2 text-lg font-semibold">{profile.fullName}</h2>
<p className="text-secondary-text">{profile.roleName}</p>
</div>
{/* Contact details */}
<div className="space-y-3">
<MobileField icon={Mail} label="Email" value={profile.email} />
<MobileField icon={Phone} label="Phone" value={profile.phone} editable />
<MobileField icon={MapPin} label="Address" value={profile.address} editable />
</div>
</div>
)
Related Documentation
- Changing Password - Update your password
- Preferences - Notification settings
- Dashboard - Staff dashboard
- Mobile App - Mobile guide
Source Files:
src/app/api/staff/profile/route.ts- Profile API endpointsrc/lib/validation/staff.schemas.ts- Validation schemassrc/types/database.types.ts- Staff type definitionssrc/lib/utils/email.ts- Email normalisation utilities