Skip to main content

Company Status

This guide covers how to manage company status, including activating and deactivating companies and understanding the effects on users and data.


Overview

What is Company Status?

Company status controls whether a company is operational on the platform:

StatusMeaningEffect
ActiveCompany is operationalFull access for all users
InactiveCompany is suspendedAccess restricted

Status Effects

When a company is deactivated:

ComponentEffect
Company ManagersCannot login
Staff MembersCannot login
DataPreserved but inaccessible
SchedulesFrozen, no new entries
ReportsCannot be generated
API AccessBlocked for company users

Toggle Status API

Endpoint

Endpoint: POST /api/companies/[companyId]/toggle-status

// Source: src/app/api/companies/[companyId]/toggle-status/route.ts:19-51
export async function POST(request: NextRequest, { params }: RouteParams) {
try {
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')
}

const body = await request.json()
const validationResult = toggleStatusSchema.safeParse(body)

if (!validationResult.success) {
return ApiResponses.badRequest('Invalid request data', validationResult.error.flatten().fieldErrors)
}

// Update company status
const result = await companyDB.updateCompany(
params.companyId,
{ is_active: validationResult.data.active }
)

return ApiResponses.success(result.data, { timing: result.timing.duration })

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

Validation Schema

// Source: src/app/api/companies/[companyId]/toggle-status/route.ts:15-17
const toggleStatusSchema = z.object({
active: z.boolean()
})

Request Format

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

return response.json()
}

// Deactivate company
await toggleCompanyStatus('company-uuid', false)

// Activate company
await toggleCompanyStatus('company-uuid', true)

Response Format

// Successful toggle response
{
success: true,
data: {
id: "company-uuid",
name: "Example Practice",
is_active: false, // or true
updated_at: "2025-01-14T10:30:00Z"
},
timing: "45ms"
}

Activating Companies

When to Activate

ScenarioAction
New company setup completeActivate to enable access
Subscription renewedReactivate after payment
Issue resolvedRestore access after fix
Trial period startedEnable for evaluation

Activation Process

1. Navigate to company details
2. Review company status
3. Click "Activate Company"
4. Confirm activation
5. Company and users gain access

Activation Effects

When a company is activated:

ComponentBeforeAfter
LoginBlockedEnabled
DashboardInaccessibleFull access
SchedulingFrozenOperational
Time TrackingDisabledEnabled
ReportsBlockedAvailable

API Example

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

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

return response.json()
}

Deactivating Companies

When to Deactivate

ScenarioPriorityDuration
Non-paymentHighUntil resolved
Contract endedHighPermanent
Security incidentCriticalUntil resolved
Requested by companyMediumAs requested
Compliance issueHighUntil resolved

Deactivation Considerations

Before deactivating, consider:

  1. Active Staff - Users will lose access immediately
  2. Scheduled Shifts - Future shifts remain but are inaccessible
  3. Pending Requests - Leave requests become frozen
  4. Time Entries - Current week entries may be affected
  5. Data Retention - All data is preserved
Impact on Operations

Deactivating a company immediately affects all users. Consider notifying the Company Manager before deactivation when possible.

Deactivation Process

1. Navigate to company details
2. Review active users and data
3. Click "Deactivate Company"
4. Enter reason for deactivation
5. Confirm deactivation
6. Notification sent to managers

API Example

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

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

return response.json()
}

Status Check

Viewing Company Status

// Check company status
const getCompanyStatus = async (companyId: string) => {
const response = await fetch(`/api/companies/${companyId}`)
const { data } = await response.json()

return {
id: data.id,
name: data.name,
isActive: data.isActive,
updatedAt: data.updatedAt
}
}

Status Indicators

IndicatorColourMeaning
Active🟢 GreenCompany operational
Inactive🔴 RedCompany suspended

Status Display Component

// Company status badge
const CompanyStatusBadge = ({ isActive }: { isActive: boolean }) => (
<span className={`
px-2 py-1 rounded text-sm font-medium
${isActive
? 'bg-green-500/20 text-green-300'
: 'bg-red-500/20 text-red-300'
}
`}>
{isActive ? 'Active' : 'Inactive'}
</span>
)

Access Control

User Impact

When company is inactive:

User RoleCan LoginCan Access Data
System Admin✅ Yes✅ Yes (admin view)
Company Manager❌ No❌ No
Staff❌ No❌ No

Login Behaviour

// Login check for inactive company
const checkCompanyAccess = async (user: User) => {
if (user.role === 'SYSTEM_ADMIN') {
return true // System admins always have access
}

const { data: company } = await supabase
.from('companies')
.select('is_active')
.eq('id', user.company_id)
.single()

if (!company?.is_active) {
throw new Error('Your company account has been suspended. Please contact support.')
}

return true
}

Error Messages

// Error shown to users of inactive company
{
error: "Account Suspended",
message: "Your company account has been suspended. Please contact your administrator for assistance.",
code: "COMPANY_INACTIVE"
}

Bulk Status Operations

Update Multiple Companies

// Source: src/lib/validation/company.schemas.ts:191-199
export const bulkUpdateCompanySchema = z.object({
company_ids: z.array(uuidSchema).min(1, 'At least one company ID is required'),
updates: z.object({
is_active: z.boolean().optional(),
industry_template_id: uuidSchema.optional()
}).refine(data => Object.keys(data).length > 0, {
message: 'At least one field must be provided for update'
})
})

Bulk Toggle Example

// Deactivate multiple companies
const bulkDeactivate = async (companyIds: string[]) => {
const results = await Promise.all(
companyIds.map(id =>
fetch(`/api/companies/${id}/toggle-status`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ active: false })
})
)
)

return results
}

Status History

Tracking Changes

EventLogged Data
ActivationTimestamp, admin ID, reason
DeactivationTimestamp, admin ID, reason

Activity Log Entry

// Status change activity log
{
action: 'company_status_changed',
company_id: 'company-uuid',
created_by: 'admin-uuid',
details: {
previous_status: true,
new_status: false,
reason: 'Non-payment'
},
created_at: '2025-01-14T10:30:00Z'
}

Viewing Status History

// Get company status history
const getStatusHistory = async (companyId: string) => {
const { data } = await supabase
.from('company_manager_activity_logs')
.select('*')
.eq('company_id', companyId)
.eq('action', 'company_status_changed')
.order('created_at', { ascending: false })

return data
}

UI Components

Status Toggle Button

// Company status toggle
const StatusToggle = ({ company, onToggle }) => {
const [loading, setLoading] = useState(false)

const handleToggle = async () => {
setLoading(true)

try {
await onToggle(!company.isActive)
} finally {
setLoading(false)
}
}

return (
<button
onClick={handleToggle}
disabled={loading}
className={`
px-4 py-2 rounded font-medium
${company.isActive
? 'bg-red-500/20 text-red-300 hover:bg-red-500/30'
: 'bg-green-500/20 text-green-300 hover:bg-green-500/30'
}
`}
>
{loading
? 'Updating...'
: company.isActive ? 'Deactivate' : 'Activate'
}
</button>
)
}

Confirmation Modal

// Deactivation confirmation modal
const DeactivateConfirmModal = ({ company, onConfirm, onCancel }) => (
<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">
Deactivate Company
</h2>

<p className="text-secondary-text mb-4">
Are you sure you want to deactivate <strong>{company.name}</strong>?
</p>

<div className="bg-red-500/10 border border-red-500/20 rounded p-3 mb-4">
<p className="text-sm text-red-300">
This will immediately block access for all company users.
</p>
</div>

<div className="flex gap-3">
<button onClick={onCancel} className="form-button secondary flex-1">
Cancel
</button>
<button onClick={onConfirm} className="form-button danger flex-1">
Deactivate
</button>
</div>
</div>
</div>
)

Best Practices

Before Deactivation

  1. Notify Company Manager - Give advance warning when possible
  2. Document Reason - Record why company is being deactivated
  3. Review Active Data - Check for pending operations
  4. Consider Timing - Avoid mid-shift deactivation
  5. Prepare Communication - Draft user notification

After Reactivation

  1. Notify Company Manager - Confirm access restored
  2. Verify Access - Test login functionality
  3. Check Data Integrity - Ensure no data corruption
  4. Review Pending Items - Process frozen requests
  5. Update Records - Document reactivation

Troubleshooting

Common Issues

IssueCauseSolution
Toggle failedNetwork errorRetry operation
Users still blockedCache delayWait 5 minutes or clear cache
Status not updatingDatabase errorCheck Supabase logs
Forbidden errorNot System AdminVerify role permissions

Error Messages

ErrorMeaningAction
"Invalid request data"Missing active fieldInclude { active: boolean }
"Forbidden"Not authorisedLogin as System Admin
"Company not found"Invalid company IDVerify company exists
"Database error"Server issueContact support


Source Files:

  • src/app/api/companies/[companyId]/toggle-status/route.ts - Status toggle API
  • src/lib/db/company.ts - Database operations
  • src/lib/validation/company.schemas.ts - Validation schemas
  • src/types/database.types.ts - Database type definitions