Skip to main content

Company Deletion

This guide covers how to delete companies, understanding soft delete behaviour, and the effects on users and data.


Overview

What is Company Deletion?

Shyfts uses soft deletion for companies:

AspectDescription
MethodSoft delete (sets is_active: false)
Data RetentionAll data preserved
ReversibilityCan be reactivated
User ImpactAll users lose access immediately

Why Soft Delete?

ReasonBenefit
Data PreservationHistorical records maintained
ComplianceMeets data retention requirements
RecoveryAccidental deletions recoverable
Audit TrailComplete history preserved

Delete Company API

Endpoint

Endpoint: DELETE /api/companies/[companyId]

// Source: src/app/api/companies/[companyId]/route.ts:166-192
export async function DELETE(_request: NextRequest, { params }: RouteParams) {
try {
const resolvedParams = await params
const companyId = resolvedParams.companyId

const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
return ApiResponses.unauthorized()
}

// Check if user is system admin
if (getAuthRole(user) !== 'SYSTEM_ADMIN') {
return ApiResponses.forbidden('Forbidden - System Admin access required')
}

// Soft delete the company
const result = await companyDB.deleteCompany(companyId)

return ApiResponses.success({ message: 'Company deleted successfully', ...result.data })

} catch (error) {
return handleApiError(error)
}
}

Soft Delete Implementation

// Source: src/lib/db/company.db.ts:212-223
export async function deleteCompany(id: string): Promise<{ data: CompanyComponentType, timing: { duration: number } }> {
const { result, timing } = await trackPerformance('deleteCompany', async () => {
const supabase = await createClient();
const { data: company, error } = await supabase
.from('companies')
.update({ is_active: false, updated_at: new Date().toISOString() })
.eq('id', id)
.select(`*,
industry_templates (*)`)
.single();

if (error) handleDBError(error);
// ... adapter conversion
});
}

Request Format

// Delete company request
const deleteCompany = async (companyId: string) => {
const response = await fetch(`/api/companies/${companyId}`, {
method: 'DELETE'
})

if (!response.ok) {
throw new Error('Failed to delete company')
}

return response.json()
}

Response Format

// Successful deletion response
{
success: true,
data: {
message: "Company deleted successfully",
id: "company-uuid",
name: "Example Practice",
is_active: false,
updated_at: "2025-01-14T10:30:00Z"
}
}

Deletion Effects

Immediate Effects

When a company is deleted (soft delete):

ComponentEffect
Company StatusSet to inactive
Company ManagersCannot login
Staff MembersCannot login
SchedulesFrozen, inaccessible
DataPreserved but inaccessible
API AccessBlocked

Data Preservation

Data TypeStatusAccessibility
Company RecordPreservedSystem Admin only
Staff RecordsPreservedSystem Admin only
Shift HistoryPreservedNot accessible
Leave RecordsPreservedNot accessible
Time EntriesPreservedNot accessible
DocumentsPreservedNot accessible

Pre-Deletion Checklist

Before Deleting

Review these items before deletion:

ItemActionPriority
Active UsersCount and notifyHigh
Pending ShiftsReview future schedulesHigh
Leave RequestsProcess pending requestsMedium
Outstanding TimeApprove/reject entriesMedium
DocumentsExport if neededLow
ReportsGenerate final reportsLow

Pre-Deletion Report

// Get pre-deletion summary
const getPreDeletionReport = async (companyId: string) => {
const [company, staffCount, pendingShifts, pendingLeave] = await Promise.all([
fetch(`/api/companies/${companyId}`).then(r => r.json()),
fetch(`/api/companies/${companyId}/staff/count`).then(r => r.json()),
fetch(`/api/companies/${companyId}/shifts?status=pending`).then(r => r.json()),
fetch(`/api/companies/${companyId}/leave?status=pending`).then(r => r.json())
])

return {
company: company.data,
totalStaff: staffCount.data.count,
pendingShifts: pendingShifts.data.length,
pendingLeave: pendingLeave.data.length
}
}

Deletion Process

Step-by-Step

1. Navigate to company details
2. Review pre-deletion checklist
3. Click "Delete Company" button
4. Review impact summary
5. Enter confirmation (company name)
6. Click "Confirm Delete"
7. Company soft deleted
8. Users notified

Confirmation Requirements

RequirementPurpose
Enter Company NamePrevents accidental deletion
Review ImpactUnderstand consequences
Double ConfirmationExtra safety measure

Deletion Confirmation Modal

Component Example

// Delete confirmation modal
const DeleteConfirmModal = ({ company, onConfirm, onCancel }) => {
const [confirmName, setConfirmName] = useState('')
const [loading, setLoading] = useState(false)

const isConfirmed = confirmName.toLowerCase() === company.name.toLowerCase()

const handleDelete = async () => {
if (!isConfirmed) return

setLoading(true)
try {
await onConfirm()
} finally {
setLoading(false)
}
}

return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="card-glass p-6 max-w-md w-full mx-4">
<h2 className="text-xl font-semibold text-red-400 mb-4">
Delete Company
</h2>

<div className="space-y-4">
<p className="text-secondary-text">
This will immediately block access for all {company.staffCount} users.
</p>

<div className="bg-red-500/10 border border-red-500/20 rounded p-3">
<p className="text-sm text-red-300">
<strong>Warning:</strong> All company managers and staff will be
unable to login. This action can be reversed by reactivating the company.
</p>
</div>

<div>
<label className="block text-sm font-medium mb-2">
Type <strong>{company.name}</strong> to confirm
</label>
<input
type="text"
value={confirmName}
onChange={(e) => setConfirmName(e.target.value)}
className="form-input w-full"
placeholder="Enter company name"
/>
</div>

<div className="flex gap-3 pt-2">
<button
onClick={onCancel}
className="form-button secondary flex-1"
disabled={loading}
>
Cancel
</button>
<button
onClick={handleDelete}
disabled={!isConfirmed || loading}
className="form-button danger flex-1"
>
{loading ? 'Deleting...' : 'Delete Company'}
</button>
</div>
</div>
</div>
</div>
)
}

Recovery Options

Reactivating Deleted Companies

Since deletion is soft, companies can be reactivated:

// Reactivate a deleted company
const reactivateCompany = async (companyId: string) => {
const response = await fetch(`/api/companies/${companyId}/toggle-status`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ active: true })
})

return response.json()
}

Recovery Process

1. Navigate to Admin Portal
2. View inactive companies filter
3. Select the deleted company
4. Click "Reactivate Company"
5. Confirm reactivation
6. Company and users regain access

Recovery Limitations

LimitationDescription
Time LimitNo hard limit (data preserved indefinitely)
Data IntegrityAll original data restored
User AccountsAll accounts restored
PermissionsOriginal permissions restored

Permanent Deletion

Hard Delete (Future Feature)

Not Currently Available

Permanent deletion is not currently implemented. All deletions are soft deletes that can be reversed.

When Permanent Delete May Be Needed

ScenarioReason
GDPR RequestData subject right to erasure
ComplianceRegulatory requirement
StorageLong-term inactive data cleanup

Data Subject Requests

For GDPR erasure requests:

  1. Document Request - Record the formal request
  2. Verify Identity - Confirm requester identity
  3. Assess Scope - Determine what data to erase
  4. Contact Support - Manual database intervention required
  5. Confirm Deletion - Provide confirmation to requester

Audit Trail

Deletion Logging

All deletions are logged:

// Deletion activity log entry
{
action: 'company_deleted',
company_id: 'company-uuid',
created_by: 'system-admin-uuid',
details: {
company_name: 'Example Practice',
staff_count: 15,
manager_count: 2,
reason: 'Contract terminated'
},
created_at: '2025-01-14T10:30:00Z',
ip_address: '192.168.1.1'
}

Viewing Deletion History

// Get deletion history
const getDeletionHistory = async () => {
const { data } = await supabase
.from('system_activity_logs')
.select('*')
.eq('action', 'company_deleted')
.order('created_at', { ascending: false })

return data
}

User Notifications

Notification Flow

When a company is deleted:

RecipientNotificationMethod
Company ManagersAccount suspendedEmail (if configured)
Staff MembersAccess revokedEmail (if configured)
System AdminsDeletion confirmedDashboard notification

Notification Template

// Deletion notification content
{
subject: "Your Shyfts Account Has Been Suspended",
body: `
Dear {firstName},

Your company ({companyName}) account on Shyfts has been suspended.
You will no longer be able to access the platform.

If you believe this is an error, please contact your administrator.

Regards,
Shyfts Team
`
}

Best Practices

Before Deletion

PracticeDescription
Export DataGenerate reports for records
Notify UsersGive advance warning when possible
Process PendingHandle outstanding requests
Document ReasonRecord why company was deleted

After Deletion

PracticeDescription
Verify Access BlockedConfirm users cannot login
Update RecordsNote deletion in external systems
Retain DocumentationKeep deletion records
Monitor Recovery RequestsWatch for reactivation needs

Troubleshooting

Common Issues

IssueCauseSolution
Deletion failedNetwork errorRetry operation
Users still have accessCache delayWait 5 minutes
Company not foundInvalid IDVerify company exists
Forbidden errorNot System AdminCheck permissions

Error Messages

ErrorMeaningAction
"Company not found"Invalid company IDVerify company exists
"Forbidden"Not authorisedLogin as System Admin
"Database error"Server issueContact support
"Deletion in progress"Already processingWait and retry

API Reference Summary

Delete Endpoint

PropertyValue
MethodDELETE
Endpoint/api/companies/[companyId]
AuthenticationRequired
Role RequiredSYSTEM_ADMIN
Soft DeleteYes (sets is_active = false)

Response Codes

CodeMeaning
200Successfully deleted
401Not authenticated
403Not authorised (not System Admin)
404Company not found
500Server error


Source Files:

  • src/app/api/companies/[companyId]/route.ts - Company delete API
  • src/lib/db/company.db.ts - Soft delete implementation
  • src/types/database.types.ts - Database type definitions