Deactivating Staff
This guide covers the staff deactivation process, including soft deletion, impact on scheduling, data retention, and reactivation procedures.
Understanding Deactivation
Soft Delete Approach
Shyfts uses soft deletion for staff members rather than permanent deletion. This means:
- Data Preserved - All staff records remain in the database
- Historical Integrity - Past shifts and reports stay accurate
- Audit Trail - Employment history is maintained
- Reversible - Staff can be reactivated if needed
Deactivation vs Deletion
| Action | Effect | Reversible |
|---|---|---|
| Deactivate | Sets is_active = false | ✅ Yes |
| Delete | Removes record entirely | ❌ No |
Shyfts only supports deactivation to maintain data integrity.
Deactivation Process
From Staff List
- Navigate to Dashboard → Staff Management
- Find the staff member to deactivate
- Click the Delete/Deactivate button (trash icon)
- Confirm the action in the dialog
// Source: src/components/staff/StaffList.tsx:58-82
const handleDelete = async (staffId: string) => {
if (!confirm('Are you sure you want to deactivate this staff member?')) {
return;
}
try {
setDeletingId(staffId);
const response = await fetch(`/api/staff/${staffId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ is_active: false }),
});
if (!response.ok) {
throw new Error('Failed to deactivate staff member');
}
await loadStaff();
} catch (err) {
console.error('Error deactivating staff:', err);
alert('Failed to deactivate staff member');
} finally {
setDeletingId(null);
}
};
Confirmation Dialog
The system displays a confirmation dialog before deactivation:
// Source: src/components/staff/StaffList.tsx:59
if (!confirm('Are you sure you want to deactivate this staff member?')) {
return;
}
Deactivation requires explicit confirmation. This prevents accidental removal of staff members from the active roster.
API Endpoint
Deactivate Staff
Endpoint: PATCH /api/staff/[id]
Request Body:
{
"is_active": false
}
Response:
{
"success": true,
"data": {
"id": "staff-uuid",
"first_name": "John",
"last_name": "Smith",
"is_active": false,
"updated_at": "2025-01-13T10:30:00Z"
}
}
Database Update
The deactivation updates the staff table:
UPDATE staff
SET is_active = false,
updated_at = NOW()
WHERE id = 'staff-uuid';
Impact of Deactivation
Immediate Effects
| Area | Impact |
|---|---|
| Staff List | Staff member hidden from active list |
| Scheduling | Cannot be assigned to new shifts |
| Dropdowns | Removed from staff selection options |
| Login | Account access may be revoked |
Preserved Data
| Data Type | Status |
|---|---|
| Historical Shifts | Preserved and visible |
| Leave Records | Maintained for reporting |
| Documents | Retained in storage |
| Time Records | Kept for payroll/audit |
| Profile Data | Unchanged |
Calendar Impact
Deactivated staff:
- Future shifts - Should be reassigned or cancelled
- Past shifts - Remain visible in historical views
- Reports - Continue to appear in historical reports
Staff List Filtering
Active/Inactive Filter
The staff list can filter by status:
// Source: src/components/staff/CompanyStaffManagement.tsx:202-203
(filterStatus === 'active' && member.isActive) ||
(filterStatus === 'inactive' && !member.isActive)
Status Display
// Source: src/components/staff/CompanyStaffManagement.tsx:413
{staff.filter(s => s.isActive).length}
Filter Options
| Filter | Shows |
|---|---|
| All | Active and inactive staff |
| Active | Only is_active = true |
| Inactive | Only is_active = false |
Before Deactivation
Pre-Deactivation Checklist
Before deactivating a staff member, ensure:
-
Future Shifts Reassigned
- Check for upcoming shifts
- Reassign or cancel as needed
-
Leave Requests Resolved
- Approve or reject pending requests
- Cancel approved future leave
-
Outstanding Time Records
- Review any pending timesheet submissions
- Approve completed time records
-
Documents Collected
- Ensure all company property returned
- Collect any required exit documentation
-
Access Revoked
- Deactivation will prevent login
- Consider timing of access removal
Finding Future Shifts
SELECT s.*, r.name as room_name
FROM shifts s
JOIN rooms r ON s.room_id = r.id
WHERE s.staff_id = 'staff-uuid'
AND s.start_time > NOW()
AND s.status = 'SCHEDULED'
ORDER BY s.start_time;
Offboarding Workflow
Recommended Steps
-
Notify HR/Management
- Document reason for departure
- Set last working day
-
Review Scheduled Work
- Check calendar for future shifts
- Arrange coverage or reassignment
-
Process Final Pay
- Review outstanding timesheets
- Calculate final payment
-
Collect Company Property
- Keys, badges, equipment
- Company documents
-
Document Return
- Obtain signed acknowledgments
- File exit documentation
-
Deactivate Account
- Set
is_active = false - Timing: after last working day
- Set
-
Communicate Departure
- Inform relevant team members
- Update schedules as needed
Reactivating Staff
When to Reactivate
Staff can be reactivated when:
- Employee returns from extended leave
- Rehiring a former employee
- Accidental deactivation
- Seasonal staff returning
Reactivation Process
- Navigate to Staff Management
- Filter to show Inactive staff
- Find the staff member
- Click Edit Profile
- Change status to Active
- Save changes
Reactivation API
Endpoint: PATCH /api/staff/[id]
Request Body:
{
"is_active": true
}
Post-Reactivation
After reactivating:
- Update Role - Verify role assignment is current
- Check Credentials - Ensure login access is restored
- Review Availability - Update availability schedule
- Assign Shifts - Begin scheduling as needed
Bulk Deactivation
Multiple Staff Deactivation
For bulk operations (e.g., seasonal staff):
const deactivateMultiple = async (staffIds: string[]) => {
const promises = staffIds.map(id =>
fetch(`/api/staff/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ is_active: false })
})
);
await Promise.all(promises);
};
Bulk Reactivation
Similarly for reactivation:
const reactivateMultiple = async (staffIds: string[]) => {
const promises = staffIds.map(id =>
fetch(`/api/staff/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ is_active: true })
})
);
await Promise.all(promises);
};
Data Retention
Retention Policy
Deactivated staff data is retained according to legal requirements:
| Data Type | Retention Period |
|---|---|
| Employment records | 6 years after departure |
| Payroll/tax records | 6 years |
| Disciplinary records | Duration of warning + 12 months |
| Training records | 3 years |
| Medical records | 40 years (occupational health) |
Permanent Deletion
If permanent deletion is required (e.g., GDPR request):
- Verify eligibility - Check legal retention requirements
- Document request - Record the deletion request
- Remove from database - Delete staff record
- Clear storage - Remove associated documents
- Audit log - Record deletion in audit trail
Permanent deletion should only be performed when legally required and after verification that retention periods have been met.
Access Control
Login Status
When a staff member is deactivated:
| Scenario | Access |
|---|---|
| Active session | May continue until logout |
| New login attempt | Should be prevented |
| Password reset | Should be blocked |
Immediate Lockout
For immediate access revocation, consider:
- Force logout - Invalidate active sessions
- Password reset - Change password to random value
- Account lock - Set account lock flag
Reporting
Including Inactive Staff
Reports can include or exclude inactive staff:
// Include all staff
const allStaff = await supabase
.from('staff')
.select('*')
.eq('company_id', companyId);
// Active only (default)
const activeStaff = await supabase
.from('staff')
.select('*')
.eq('company_id', companyId)
.eq('is_active', true);
// Inactive only
const inactiveStaff = await supabase
.from('staff')
.select('*')
.eq('company_id', companyId)
.eq('is_active', false);
Historical Reports
For historical accuracy, reports typically include all staff who were active during the reporting period, regardless of current status.
Best Practices
Deactivation Timing
| Scenario | Recommended Timing |
|---|---|
| Resignation | Last working day |
| Termination | Effective immediately |
| Retirement | Last working day |
| Leave of absence | Start of leave (if extended) |
| Seasonal end | End of season |
Communication
- Internal - Inform relevant team members
- Scheduling - Update future rotas promptly
- Handover - Ensure knowledge transfer
- Documentation - Record departure details
Compliance
- Follow company policy - Adhere to HR procedures
- Document reasons - Maintain departure records
- Exit interviews - Conduct when appropriate
- Reference requests - Prepare for future references
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Cannot find inactive staff | Change filter to "All" or "Inactive" |
| Deactivation failed | Check network connection, verify permissions |
| Staff still showing in dropdowns | Refresh page, clear cache |
| Future shifts not cancelled | Manually cancel or reassign shifts |
Error Messages
| Error | Cause | Solution |
|---|---|---|
| "Failed to deactivate" | API error | Check console for details |
| "Permission denied" | Insufficient rights | Contact administrator |
| "Staff not found" | Invalid ID | Refresh staff list |
Related Documentation
- Adding Staff - Create new staff
- Editing Staff - Update staff profiles
- Staff Roles - Role management
- Staff Documents - Document management
Source Files:
src/components/staff/StaffList.tsx- Deactivation handlersrc/components/staff/CompanyStaffManagement.tsx- Status filteringsrc/app/api/staff/[id]/route.ts- Staff update APIsrc/types/database.types.ts- Staff type definitions