Skip to main content

Industry Templates

This guide covers how to view, manage, and understand industry templates that enable Shyfts's multi-industry support.


Overview

What Are Industry Templates?

Industry templates are pre-configured setups that customise Shyfts for different business types:

TemplateDisplay NameTarget Industry
gp_practiceGP PracticeMedical practices
dental_practiceDental PracticeDental clinics
restaurantRestaurantFood service
officeOfficeGeneral business

Template Components

Each template defines:

ComponentPurpose
Staff RolesRole definitions with hierarchy levels
Room TypesWorkspace/area configurations
TerminologyIndustry-specific language
PermissionsRole-based access rights
Default SettingsPre-configured options

Default Templates

GP Practice Template

// Source: src/app/api/seed-templates/route.ts:9-93
{
name: 'gp_practice',
display_name: 'GP Practice',
staff_roles: {
gp: {
color: '#4299E1',
permissions: ['MANAGE_PATIENTS', 'PRESCRIBE', 'VIEW_MEDICAL_RECORDS'],
name: 'GP',
hierarchy_level: 1
},
nurse: {
color: '#48BB78',
permissions: ['ASSIST_PATIENTS', 'BASIC_CARE', 'VIEW_SCHEDULES'],
name: 'Nurse',
hierarchy_level: 2
},
hca: {
color: '#9f7aea',
permissions: ['HEALTHCARE_ASSISTANCE', 'PATIENT_SUPPORT'],
name: 'HCA',
hierarchy_level: 3
},
receptionist: {
color: '#ED8936',
permissions: ['RECEPTION', 'ADMINISTRATION', 'MANAGE_APPOINTMENTS'],
name: 'Receptionist',
hierarchy_level: 3
},
clinic_administrator: {
color: '#FF5733',
permissions: ['MANAGE_STAFF', 'HR', 'OPERATIONS', 'FINANCIAL_OVERSIGHT'],
name: 'Practice Administrator',
hierarchy_level: 1
}
},
terminology: {
staff: 'Staff',
company: 'Practice',
manager: 'Practice Administrator',
shifts: 'Surgery Sessions',
rooms: 'Consultation Rooms'
}
}

GP Practice Roles

RoleColourHierarchyPermissions
GPBlue (#4299E1)1 (Top)MANAGE_PATIENTS, PRESCRIBE, VIEW_MEDICAL_RECORDS
Practice AdministratorRed (#FF5733)1 (Top)MANAGE_STAFF, HR, OPERATIONS, FINANCIAL_OVERSIGHT
NurseGreen (#48BB78)2ASSIST_PATIENTS, BASIC_CARE, VIEW_SCHEDULES
HCAPurple (#9f7aea)3HEALTHCARE_ASSISTANCE, PATIENT_SUPPORT
ReceptionistOrange (#ED8936)3RECEPTION, ADMINISTRATION, MANAGE_APPOINTMENTS
AdminIndigo (#667eea)3ADMINISTRATION, DATA_MANAGEMENT

GP Practice Room Types

RoomCapacityEquipment
GP Room2Examination table, computer, medical equipment
HCA Room2Treatment table, medical supplies
Reception3Reception desk, phone system, computer
Non Clinical Room4Desk, computer, meeting table
WFH1Remote access

Dental Practice Template

// Source: src/app/api/seed-templates/route.ts:95-159
{
name: 'dental_practice',
display_name: 'Dental Practice',
staff_roles: {
dentist: {
color: '#0EA5E9',
permissions: ['PERFORM_PROCEDURES', 'DIAGNOSE', 'PRESCRIBE'],
name: 'Dentist',
hierarchy_level: 1
},
dental_hygienist: {
color: '#10B981',
permissions: ['CLEANINGS', 'PATIENT_EDUCATION', 'BASIC_PROCEDURES'],
name: 'Dental Hygienist',
hierarchy_level: 2
},
dental_assistant: {
color: '#F59E0B',
permissions: ['ASSIST_PROCEDURES', 'PATIENT_PREP', 'EQUIPMENT_SETUP'],
name: 'Dental Assistant',
hierarchy_level: 3
},
clinic_manager: {
color: '#8B5CF6',
permissions: ['MANAGE_STAFF', 'OPERATIONS', 'FINANCIAL_MANAGEMENT'],
name: 'Practice Manager',
hierarchy_level: 1
}
},
terminology: {
staff: 'Dental Team',
company: 'Practice',
manager: 'Practice Manager',
shifts: 'Appointment Blocks',
rooms: 'Treatment Rooms'
}
}

Dental Practice Roles

RoleColourHierarchyPermissions
DentistSky Blue (#0EA5E9)1 (Top)PERFORM_PROCEDURES, DIAGNOSE, PRESCRIBE
Practice ManagerViolet (#8B5CF6)1 (Top)MANAGE_STAFF, OPERATIONS, FINANCIAL_MANAGEMENT
Dental HygienistEmerald (#10B981)2CLEANINGS, PATIENT_EDUCATION, BASIC_PROCEDURES
Dental AssistantAmber (#F59E0B)3ASSIST_PROCEDURES, PATIENT_PREP, EQUIPMENT_SETUP
ReceptionistPink (#EC4899)3SCHEDULE_APPOINTMENTS, BILLING, PATIENT_RECORDS

Dental Practice Room Types

RoomCapacityEquipment
Treatment Room2Dental chair, X-ray, dental tools, suction
Hygiene Room2Dental chair, cleaning tools, polishing equipment
Consultation Room3Desk, computer, patient education materials
Waiting Area15Seating, reception desk, magazines

Restaurant Template

// Source: src/app/api/seed-templates/route.ts:160-219
{
name: 'restaurant',
display_name: 'Restaurant',
staff_roles: {
head_chef: {
color: '#DC2626',
permissions: ['MANAGE_KITCHEN', 'MENU_PLANNING', 'SUPERVISE_COOKS'],
name: 'Head Chef',
hierarchy_level: 1
},
sous_chef: {
color: '#EA580C',
permissions: ['ASSIST_HEAD_CHEF', 'FOOD_PREP', 'QUALITY_CONTROL'],
name: 'Sous Chef',
hierarchy_level: 2
},
line_cook: {
color: '#F59E0B',
permissions: ['FOOD_PREPARATION', 'STATION_MANAGEMENT'],
name: 'Line Cook',
hierarchy_level: 3
},
server: {
color: '#10B981',
permissions: ['TAKE_ORDERS', 'SERVE_CUSTOMERS', 'HANDLE_PAYMENTS'],
name: 'Server',
hierarchy_level: 3
},
manager: {
color: '#8B5CF6',
permissions: ['MANAGE_STAFF', 'INVENTORY', 'CUSTOMER_SERVICE'],
name: 'Restaurant Manager',
hierarchy_level: 1
}
},
terminology: {
staff: 'Team Members',
company: 'Restaurant',
manager: 'Restaurant Manager',
shifts: 'Service Shifts',
rooms: 'Stations'
}
}

Restaurant Roles

RoleColourHierarchyPermissions
Head ChefRed (#DC2626)1 (Top)MANAGE_KITCHEN, MENU_PLANNING, SUPERVISE_COOKS
Restaurant ManagerViolet (#8B5CF6)1 (Top)MANAGE_STAFF, INVENTORY, CUSTOMER_SERVICE
Sous ChefOrange (#EA580C)2ASSIST_HEAD_CHEF, FOOD_PREP, QUALITY_CONTROL
Line CookAmber (#F59E0B)3FOOD_PREPARATION, STATION_MANAGEMENT
ServerEmerald (#10B981)3TAKE_ORDERS, SERVE_CUSTOMERS, HANDLE_PAYMENTS
Host/HostessBlue (#3B82F6)3GREET_CUSTOMERS, MANAGE_SEATING, RESERVATIONS

Restaurant Room Types

RoomCapacityEquipment
Kitchen8Stoves, ovens, prep stations, dishwasher
Dining Room50Tables, chairs, POS system
Bar Area15Bar stools, drink station, cash register
Prep Area4Cutting boards, storage, refrigeration

Office Template

// Source: src/app/api/seed-templates/route.ts:220-273
{
name: 'office',
display_name: 'Office',
staff_roles: {
manager: {
color: '#7C3AED',
permissions: ['MANAGE_TEAM', 'APPROVE_REQUESTS', 'STRATEGIC_PLANNING'],
name: 'Manager',
hierarchy_level: 1
},
supervisor: {
color: '#2563EB',
permissions: ['SUPERVISE_STAFF', 'DAILY_OPERATIONS', 'REPORTING'],
name: 'Supervisor',
hierarchy_level: 2
},
employee: {
color: '#059669',
permissions: ['COMPLETE_TASKS', 'ATTEND_MEETINGS', 'BASIC_ADMIN'],
name: 'Employee',
hierarchy_level: 3
},
admin_assistant: {
color: '#EA580C',
permissions: ['ADMIN_SUPPORT', 'SCHEDULING', 'CORRESPONDENCE'],
name: 'Administrative Assistant',
hierarchy_level: 3
}
},
terminology: {
staff: 'Employees',
company: 'Office',
manager: 'Manager',
shifts: 'Work Shifts',
rooms: 'Workspaces'
}
}

Office Roles

RoleColourHierarchyPermissions
ManagerPurple (#7C3AED)1 (Top)MANAGE_TEAM, APPROVE_REQUESTS, STRATEGIC_PLANNING
SupervisorBlue (#2563EB)2SUPERVISE_STAFF, DAILY_OPERATIONS, REPORTING
EmployeeEmerald (#059669)3COMPLETE_TASKS, ATTEND_MEETINGS, BASIC_ADMIN
Admin AssistantOrange (#EA580C)3ADMIN_SUPPORT, SCHEDULING, CORRESPONDENCE
InternRed (#DC2626)4LEARNING_TASKS, ASSIST_PROJECTS, BASIC_DUTIES

Office Room Types

RoomCapacityEquipment
Office Space10Desks, computers, phones, filing cabinets
Meeting Room12Conference table, projector, whiteboard, video conference
Reception Area5Reception desk, seating, phone system
Break Room20Tables, chairs, kitchen facilities, vending machines

Template Data Structure

Database Schema

// Source: src/types/database.types.ts:761-795
industry_templates: {
Row: {
id: string
name: string
display_name: string
description: string | null
staff_roles: Json
room_types: Json
terminology: Json
email_templates: Json | null
default_settings: Json | null
is_active: boolean | null
created_at: string | null
updated_at: string | null
}
}

TypeScript Interfaces

// Source: src/types/industry-templates.ts:8-54
interface StaffRoleTemplate {
display_name: string
color: string
permissions: string[]
hierarchy_level: number
can_manage_staff?: boolean
can_manage_schedules?: boolean
can_view_reports?: boolean
}

interface RoomTypeTemplate {
display_name: string
capacity: number
equipment: string[]
}

interface IndustryTerminology {
staff: string
staffPlural: string
customer: string
customerPlural: string
appointment: string
appointmentPlural: string
[key: string]: string
}

interface IndustryTemplate {
id: string
name: string
display_name: string
description?: string | null
staff_roles: Record<string, StaffRoleTemplate>
room_types: Record<string, RoomTypeTemplate>
terminology: IndustryTerminology
email_templates?: Record<string, EmailTemplate> | null
default_settings?: Record<string, unknown> | null
is_active?: boolean | null
created_at?: string | null
updated_at?: string | null
}

Template API

Get Template by ID

Endpoint: GET /api/industry-templates/[id]

// Source: src/app/api/industry-templates/[id]/route.ts:8-39
export async function GET(
_request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const supabase = await createClient()

// Check authentication
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return ApiResponses.unauthorized()
}

const { data: template, error } = await supabase
.from('industry_templates')
.select('*')
.eq('id', params.id)
.single()

if (error) {
throw error
}

if (!template) {
return ApiResponses.notFound('Industry template not found')
}

return ApiResponses.success({ data: template })
} catch (error) {
return handleApiError(error)
}
}

Get All Templates

// Source: src/utils/api/industryTemplates.ts:36-54
export async function getIndustryTemplates(): Promise<IndustryTemplate[]> {
const { data, error } = await supabase
.from('industry_templates')
.select('*')
.order('name')

if (error) {
throw new Error(`Failed to fetch industry templates: ${error.message}`)
}

const templates: IndustryTemplate[] = []
for (const dbTemplate of data || []) {
const converted = convertDatabaseToIndustryTemplate(dbTemplate)
if (converted) {
templates.push(converted)
}
}
return templates
}

Response Format

// Template details response
{
success: true,
data: {
data: {
id: "template-uuid",
name: "gp_practice",
display_name: "GP Practice",
staff_roles: { /* role definitions */ },
room_types: { /* room definitions */ },
terminology: { /* industry terms */ },
is_active: true,
created_at: "2025-01-01T00:00:00Z"
}
}
}

Seed Templates API

Seed Default Templates

Endpoint: POST /api/seed-templates

// Source: src/app/api/seed-templates/route.ts:276-354
export async function POST() {
try {
const supabase = await createClient()
const adminClient = await createAdminClient()

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

if (!user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}

if (getAuthRole(user) !== 'SYSTEM_ADMIN') {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}

if (process.env.NODE_ENV === 'production') {
return NextResponse.json(
{ error: 'Template seeding disabled in production' },
{ status: 403 }
)
}

// Check if templates already exist
const { data: existing } = await supabase
.from('industry_templates')
.select('id, name, display_name')

// Insert templates using admin client
const { data, error } = await adminClient
.from('industry_templates')
.insert(templates)
.select()

return NextResponse.json({
success: true,
message: `Successfully seeded ${data.length} industry templates`,
templates: data.map(t => ({ id: t.id, name: t.name }))
})
} catch (error) {
return handleApiError(error)
}
}

Seed Response

// Successful seed response
{
success: true,
message: "Successfully seeded 4 industry templates",
templates: [
{ id: "uuid-1", name: "gp_practice" },
{ id: "uuid-2", name: "dental_practice" },
{ id: "uuid-3", name: "restaurant" },
{ id: "uuid-4", name: "office" }
]
}

// Templates already exist response
{
success: true,
message: "All templates already exist",
existing: [
{ name: "gp_practice", display_name: "GP Practice" },
{ name: "dental_practice", display_name: "Dental Practice" },
{ name: "restaurant", display_name: "Restaurant" },
{ name: "office", display_name: "Office" }
]
}

Template Client Utilities

Fetch Template by Name

// Source: src/utils/api/industryTemplates.ts:79-94
export async function getIndustryTemplateByName(
name: string
): Promise<IndustryTemplate | null> {
const { data, error } = await supabase
.from('industry_templates')
.select('*')
.eq('name', name)
.single()

if (error) {
if (error.code === 'PGRST116') {
return null // Template not found
}
throw new Error(`Failed to fetch industry template: ${error.message}`)
}

return convertDatabaseToIndustryTemplate(data)
}

Create Template (System Admin)

// Source: src/utils/api/industryTemplates.ts:99-127
export async function createIndustryTemplate(
data: CreateIndustryTemplateData
): Promise<IndustryTemplate> {
const dbData: TablesInsert<'industry_templates'> = {
name: data.name,
display_name: data.name,
staff_roles: data.staff_roles as unknown as Json,
room_types: data.room_types as unknown as Json,
terminology: data.terminology as unknown as Json,
email_templates: (data.email_templates || []) as unknown as Json,
default_settings: (data.default_settings || {}) as unknown as Json,
is_active: true
}

const { data: template, error } = await supabase
.from('industry_templates')
.insert([dbData])
.select()
.single()

if (error) {
throw new Error(`Failed to create industry template: ${error.message}`)
}

return convertDatabaseToIndustryTemplate(template)
}

Update Template

// Source: src/utils/api/industryTemplates.ts:132-172
export async function updateIndustryTemplate(
id: string,
data: Partial<CreateIndustryTemplateData>
): Promise<IndustryTemplate> {
const updateData: Partial<TablesInsert<'industry_templates'>> = {}

if (data.name !== undefined) updateData.name = data.name
if (data.staff_roles !== undefined) {
updateData.staff_roles = data.staff_roles as unknown as Json
}
if (data.room_types !== undefined) {
updateData.room_types = data.room_types as unknown as Json
}
if (data.terminology !== undefined) {
updateData.terminology = data.terminology as unknown as Json
}

const { data: template, error } = await supabase
.from('industry_templates')
.update(updateData)
.eq('id', id)
.select()
.single()

if (error) {
throw new Error(`Failed to update industry template: ${error.message}`)
}

return convertDatabaseToIndustryTemplate(template)
}

Delete Template

// Source: src/utils/api/industryTemplates.ts:177-186
export async function deleteIndustryTemplate(id: string): Promise<void> {
const { error } = await supabase
.from('industry_templates')
.delete()
.eq('id', id)

if (error) {
throw new Error(`Failed to delete industry template: ${error.message}`)
}
}

Terminology System

How Terminology Works

Industry templates provide context-aware language throughout the platform:

Generic TermGP PracticeDental PracticeRestaurantOffice
staffStaffDental TeamTeam MembersEmployees
companyPracticePracticeRestaurantOffice
managerPractice AdministratorPractice ManagerRestaurant ManagerManager
shiftsSurgery SessionsAppointment BlocksService ShiftsWork Shifts
roomsConsultation RoomsTreatment RoomsStationsWorkspaces

Using Terminology in Components

// Get terminology from industry context
const { terminology } = useIndustry()

// Use dynamic terms
<h1>Manage Your {terminology.staff}</h1>
<p>Schedule {terminology.shifts} for your {terminology.company}</p>

Terminology Interface

// Source: src/types/industry-templates.ts:25-33
interface IndustryTerminology {
staff: string
staffPlural: string
customer: string
customerPlural: string
appointment: string
appointmentPlural: string
[key: string]: string
}

Hierarchy Levels

Understanding Hierarchy

Each staff role has a hierarchy level that determines authority:

LevelDescriptionExample Roles
1Management/SeniorGP, Practice Manager, Head Chef
2Mid-level/SupervisorNurse, Sous Chef, Supervisor
3Standard StaffReceptionist, Server, Employee
4Entry Level/SupportTemp, Intern, Cleaner

Hierarchy Usage

// Roles with hierarchy_level = 1 are Company Managers
const { count: staffManagerCount } = await supabase
.from('staff')
.select('*, staff_roles!inner(*)', { count: 'exact', head: true })
.eq('staff_roles.hierarchy_level', 1)
.eq('is_active', true)

Template Application

When Templates Are Applied

Templates are applied during company creation:

1. Company Creation Wizard started
2. Industry template selected (e.g., GP Practice)
3. Template roles → Staff Roles table
4. Template room types → Room Types table
5. Template terminology → Company settings
6. Company uses industry-specific UI

Company-Template Relationship

// Companies reference industry templates
interface Company {
id: string
name: string
industry_template_id: string // Reference to template
// ... other fields
}
Template Changes

Once a company is created with a template, changing the template is not supported. Template changes would affect roles, rooms, and terminology in unpredictable ways.


Access Control

Who Can Manage Templates

RoleView TemplatesCreateUpdateDelete
System Admin✅ Yes✅ Yes✅ Yes✅ Yes
Company Manager✅ Yes (read-only)❌ No❌ No❌ No
Staff❌ No❌ No❌ No❌ No

Seeding Restrictions

// Seeding disabled in production
if (process.env.NODE_ENV === 'production') {
return NextResponse.json(
{ error: 'Template seeding disabled in production' },
{ status: 403 }
)
}

Best Practices

Template Management

PracticeDescription
Don't modify in productionCreate new templates instead of editing existing
Test thoroughlyTest new templates in development first
Document changesKeep records of template modifications
Backup before changesExport template data before updates

Creating Custom Templates

ConsiderationGuidance
Role namesUse clear, industry-appropriate names
HierarchyEnsure logical hierarchy (1 = top)
ColoursUse distinct, accessible colours
TerminologyMatch industry conventions

Troubleshooting

Common Issues

IssueCauseSolution
Template not foundInvalid IDVerify template exists
Seed failedTemplates existCheck existing templates first
Forbidden errorNot System AdminLogin as System Admin
Production blockedEnvironment checkUse development environment

Error Messages

ErrorMeaningAction
"Unauthorized"Not logged inLogin first
"Forbidden"Not System AdminUse admin account
"Template seeding disabled"Production environmentUse development
"Template not found"Invalid template IDVerify ID exists


Source Files:

  • src/app/api/industry-templates/[id]/route.ts - Template API endpoint
  • src/app/api/seed-templates/route.ts - Template seeding
  • src/utils/api/industryTemplates.ts - Client utilities
  • src/types/industry-templates.ts - Type definitions
  • src/types/database.types.ts - Database schema