Company Profile
This guide covers viewing and updating your company profile information, including contact details, branding, and industry-specific settings.
Overview
What is the Company Profile?
The company profile contains essential information about your organisation:
- Basic Information - Company name, industry, contact details
- Address - Business address with UK postal code validation
- Branding - Company logo and visual identity
- Industry Template - Determines terminology and default settings
- Contact Information - Phone, email, and website
Access Levels
| Role | View | Edit | Request Changes |
|---|---|---|---|
| System Admin | ✅ | ✅ | N/A |
| Company Manager | ✅ | ❌ | ✅ |
| Staff | Limited | ❌ | ❌ |
Only System Administrators can directly modify company profile settings. Company Managers can request changes which notifies all System Admins for review.
Requesting Changes
For Company Managers
If you need to update company profile information, you can submit a change request:
- Navigate to Dashboard → Company Settings → Details tab
- Click the "Request Changes" button
- Enter a detailed description of the changes needed
- Submit the request
Example Requests
Address change:
"Our business has moved. Please update the address to:
123 New Street, London, W1A 1AA"
Contact update:
"Please update the main phone number to: 020 1234 5678
The old number has been disconnected."
Name change:
"Our practice has been renamed. Please change from
'Smith Medical Centre' to 'Smith & Partners Medical Practice'"
Viewing Company Profile
Navigation
- Navigate to Settings → Company Profile from the sidebar
- The company profile page displays current settings
- View all information including industry template and statistics
Company Profile API
Endpoint: GET /api/companies/[companyId]
// Source: src/app/api/companies/[companyId]/route.ts:22-80
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ companyId: string }> }
) {
const supabase = await createClient()
const resolvedParams = await params
// Fetch company with industry template
const { data: company, error } = await supabase
.from('companies')
.select(`
*,
industry_template:industry_templates (
id,
name,
description,
staff_terminology,
room_terminology,
shift_terminology
)
`)
.eq('id', resolvedParams.companyId)
.single()
// Get staff and manager counts
const { count: staffCount } = await supabase
.from('staff')
.select('*', { count: 'exact', head: true })
.eq('company_id', resolvedParams.companyId)
.eq('is_active', true)
const { count: managerCount } = await supabase
.from('staff')
.select('*', { count: 'exact', head: true })
.eq('company_id', resolvedParams.companyId)
.eq('is_company_manager', true)
.eq('is_active', true)
// Fetch opening hours
const { data: openingHours } = await supabase
.from('company_opening_hours')
.select('*')
.eq('company_id', resolvedParams.companyId)
.order('day_of_week')
return ApiResponses.success({
...companyAdapter.toComponent(company),
staffCount: staffCount || 0,
managerCount: managerCount || 0,
openingHours: openingHours || []
})
}
Response Data
// Company profile response structure
{
id: "company-uuid",
name: "Sunrise Medical Centre",
industry_template_id: "gp-template-uuid",
industry_template: {
id: "gp-template-uuid",
name: "GP Practice",
staff_terminology: "Staff",
room_terminology: "Consultation Room",
shift_terminology: "Shift"
},
address_line_1: "123 High Street",
address_line_2: "Suite 100",
city: "London",
postcode: "SW1A 1AA",
country: "United Kingdom",
phone: "+44 20 7123 4567",
email: "info@sunrisemedical.co.uk",
website: "https://sunrisemedical.co.uk",
logo_url: "https://storage.supabase.co/...",
is_active: true,
staffCount: 25,
managerCount: 3,
openingHours: [...]
}
Company Profile Fields
Basic Information
| Field | Description | Validation |
|---|---|---|
| Name | Company/organisation name | 2-100 characters |
| Industry Template | Industry type (GP, Dental, etc.) | Valid template ID |
| Status | Active/Inactive | Boolean |
Contact Details
| Field | Description | Validation |
|---|---|---|
| Phone | Primary contact number | UK phone format |
| Primary email address | Valid email format | |
| Website | Company website URL | Valid URL (optional) |
Address Fields
| Field | Description | Validation |
|---|---|---|
| Address Line 1 | Street address | Required, max 200 chars |
| Address Line 2 | Additional address info | Optional, max 200 chars |
| City | City/Town | Required, max 100 chars |
| Postcode | UK postal code | Valid UK format |
| Country | Country name | Default: United Kingdom |
Updating Company Profile
Update API
Endpoint: PUT /api/companies/[companyId]
Only System Administrators can update company profiles.
// Source: src/app/api/companies/[companyId]/route.ts:128-164
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ companyId: string }> }
) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
// System Admin only
if (getAuthRole(user) !== 'SYSTEM_ADMIN') {
return ApiResponses.forbidden('Forbidden - System Admin access required')
}
const body = await request.json()
const validationResult = parseWithErrors(updateCompanySchema, body)
if (!validationResult.success) {
return ApiResponses.validationError(validationResult.errors)
}
const result = await companyDB.updateCompany(
resolvedParams.companyId,
validationResult.data
)
return ApiResponses.success(companyAdapter.toComponent(result))
}
Update Request Schema
// Source: src/lib/validation/company.schemas.ts:52-85
export const updateCompanySchema = z.object({
name: z.string().min(2).max(100).optional(),
industry_template_id: z.string().uuid().optional(),
address_line_1: z.string().max(200).optional(),
address_line_2: z.string().max(200).nullable().optional(),
city: z.string().max(100).optional(),
postcode: z.string()
.regex(/^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$/i, 'Invalid UK postal code')
.optional(),
country: z.string().max(100).optional(),
phone: z.string()
.regex(/^(\+44|0)\s?\d{2,4}\s?\d{3,4}\s?\d{3,4}$/, 'Invalid UK phone number')
.optional(),
email: z.string().email().optional(),
website: z.string().url().nullable().optional(),
is_active: z.boolean().optional()
})
Example Update Request
// Update company profile
const response = await fetch(`/api/companies/${companyId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: "Sunrise Medical Centre",
address_line_1: "456 New Street",
city: "Manchester",
postcode: "M1 1AA",
phone: "+44 161 123 4567",
email: "contact@sunrisemedical.co.uk"
})
})
const data = await response.json()
Validation Rules
UK Postal Code Validation
// Source: src/lib/validation/company.schemas.ts:12-15
const ukPostcodeRegex = /^[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2}$/i
// Valid examples:
// SW1A 1AA, M1 1AA, B1 1AA, EC1A 1BB
// sw1a1aa (case insensitive, spaces optional)
UK Phone Number Validation
// Source: src/lib/validation/company.schemas.ts:17-20
const ukPhoneRegex = /^(\+44|0)\s?\d{2,4}\s?\d{3,4}\s?\d{3,4}$/
// Valid examples:
// +44 20 7123 4567
// 020 7123 4567
// +44 161 123 4567
// 0161 123 4567
Email Validation
| Check | Requirement |
|---|---|
| Format | Valid email format (user@domain.tld) |
| Length | Maximum 254 characters |
| Case | Case-insensitive storage |
Logo Management
Logo Upload
Company logos are stored in Supabase Storage:
// Logo upload process
const uploadLogo = async (file: File, companyId: string) => {
const supabase = createClient()
// Generate unique filename
const filename = `${companyId}/logo-${Date.now()}.${file.type.split('/')[1]}`
// Upload to company-logos bucket
const { data, error } = await supabase.storage
.from('company-logos')
.upload(filename, file, {
cacheControl: '3600',
upsert: true
})
if (error) throw error
// Get public URL
const { data: { publicUrl } } = supabase.storage
.from('company-logos')
.getPublicUrl(filename)
// Update company with logo URL
await supabase
.from('companies')
.update({ logo_url: publicUrl })
.eq('id', companyId)
return publicUrl
}
Logo Requirements
| Requirement | Value |
|---|---|
| Format | PNG, JPG, SVG |
| Max Size | 2MB |
| Recommended | 200x200px minimum |
| Aspect Ratio | Square preferred |
Logo Display
// Logo component usage
<img
src={company.logo_url || '/default-logo.png'}
alt={`${company.name} logo`}
className="w-16 h-16 rounded-lg object-cover"
/>
Industry Templates
Available Templates
| Template | ID | Terminology |
|---|---|---|
| GP Practice | gp-template | Staff, Consultation Room, Shift |
| Dental Practice | dental-template | Team Member, Treatment Room, Appointment |
| Restaurant | restaurant-template | Crew, Section, Shift |
| Office | office-template | Employee, Meeting Room, Schedule |
Template Selection
Industry template affects:
- Terminology - Room names, staff titles, shift labels
- Default Roles - Pre-configured staff roles
- Room Types - Default room/facility types
- Shift Types - Standard shift patterns
// Industry template structure
interface IndustryTemplate {
id: string
name: string
description: string
staff_terminology: string // e.g., "Staff", "Team Member"
room_terminology: string // e.g., "Room", "Station"
shift_terminology: string // e.g., "Shift", "Schedule"
default_roles: StaffRole[]
default_room_types: RoomType[]
}
Company Statistics
Dashboard Statistics
The company profile includes real-time statistics:
| Statistic | Description |
|---|---|
| Staff Count | Total active staff members |
| Manager Count | Staff with Company Manager role |
| Room Count | Total configured rooms |
| Shift Count | Shifts in current period |
Statistics Query
// Statistics fetched with company profile
const stats = {
staffCount: await countActiveStaff(companyId),
managerCount: await countManagers(companyId),
roomCount: await countRooms(companyId),
activeShifts: await countActiveShifts(companyId)
}
Deactivating a Company
Soft Delete
Companies are soft-deleted (deactivated) rather than permanently removed:
// Source: src/app/api/companies/[companyId]/route.ts:166-193
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ companyId: string }> }
) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
// System Admin only
if (getAuthRole(user) !== 'SYSTEM_ADMIN') {
return ApiResponses.forbidden('Forbidden - System Admin access required')
}
// Soft delete - set is_active to false
const { error } = await supabase
.from('companies')
.update({ is_active: false })
.eq('id', resolvedParams.companyId)
return ApiResponses.success({ message: 'Company deactivated successfully' })
}
Deactivation Effects
When a company is deactivated:
- Login Blocked - Staff cannot log in
- Data Preserved - All data remains in database
- Reports Available - Historical reports still accessible
- Reactivation Possible - Can be reactivated by System Admin
Best Practices
For Managing Company Profile
- Keep Information Current - Update contact details promptly
- Use Professional Branding - Upload high-quality logo
- Verify Address - Ensure postal code is valid
- Complete All Fields - Fill in optional fields where applicable
For System Administrators
- Regular Audits - Review company profiles periodically
- Template Selection - Choose appropriate industry template
- Contact Verification - Verify phone and email work
- Backup Awareness - Understand soft delete preserves data
Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Cannot edit profile | Insufficient permissions | Contact System Admin |
| Invalid postcode error | Wrong format | Use valid UK format (e.g., SW1A 1AA) |
| Logo upload fails | File too large | Reduce image size below 2MB |
| Phone validation fails | Wrong format | Use UK format (+44 or 0 prefix) |
Error Messages
| Error | Meaning | Action |
|---|---|---|
| "Forbidden - System Admin access required" | Not authorised | Only System Admin can update |
| "Invalid UK postal code" | Format error | Check postal code format |
| "Company not found" | Invalid ID | Verify company ID |
| "Validation failed" | Schema error | Check all field requirements |
Related Documentation
- Opening Hours - Configure business hours
- Rooms & Facilities - Manage rooms
- Company Creation - How companies are created
Source Files:
src/app/api/companies/[companyId]/route.ts- Company CRUD APIsrc/lib/validation/company.schemas.ts- Validation schemassrc/lib/adapters/company.adapter.ts- Data transformationsrc/lib/db/company.db.ts- Database operations