Version: 2.2.1 Last Updated: January 2026 Target Project: KNII Ticketing System (Node.js 20 + Express 5 + PostgreSQL 16)
This guide explains how to customize floors and departments for your organization without modifying code.
The KNII Ticketing System is fully dynamic - all floors and departments are defined in JSON configuration files. This allows admins to customize the system for any organization before seeding the database.
Key Benefits:
- ✅ No code changes needed
- ✅ Validate configuration before database changes
- ✅ Customize for any organization type
- ✅ Protected system department (Internal)
- ✅ Idempotent seeding (safe to run multiple times)
config/seed-data/
├── floors.json # Your building's floors
├── departments.json # Your organization's departments
├── floors.example.json # Example template for reference
└── departments.example.json # Example template for reference
-
Copy example templates:
cp config/seed-data/floors.example.json config/seed-data/floors.json cp config/seed-data/departments.example.json config/seed-data/departments.json
-
Edit configuration files for your organization
-
Run seeder:
npm run seed:hospital
-
Verify floors and departments are created in the admin UI
Defines the physical floors/levels in your building.
{
"version": "1.0.0",
"description": "Floor configuration for your organization",
"floors": [
{
"name": "Basement",
"sort_order": 0,
"active": true
},
{
"name": "Ground Floor",
"sort_order": 1,
"active": true
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
| version | string | Yes | Semantic version (e.g., "1.0.0") for schema evolution |
| description | string | No | Human-readable description of this configuration |
| floors | array | Yes | Array of floor objects (must have at least 1) |
| floors[].name | string | Yes | Floor name (2-50 characters) - must be unique |
| floors[].sort_order | integer | Yes | Display order (0, 1, 2, ...) - must be unique and >= 0 |
| floors[].active | boolean | No | Whether floor is currently in use (default: true) |
-
name:
- Length: 2-50 characters
- Unique: No duplicate floor names
- Required: Cannot be empty
- Type: Must be a string
-
sort_order:
- Type: Integer (whole number)
- Range: >= 0
- Unique: No duplicate sort_order values
- Required: Cannot be null/undefined
- Purpose: Determines display order in UI
-
active:
- Type: Boolean (true/false)
- Default: true (if omitted)
- Optional: Can be omitted
Hospital Building (8 floors):
{
"version": "1.0.0",
"floors": [
{"name": "Basement", "sort_order": 0, "active": true},
{"name": "Ground Floor", "sort_order": 1, "active": true},
{"name": "1st Floor", "sort_order": 2, "active": true},
{"name": "2nd Floor", "sort_order": 3, "active": true},
{"name": "3rd Floor", "sort_order": 4, "active": true},
{"name": "4th Floor", "sort_order": 5, "active": true},
{"name": "5th Floor", "sort_order": 6, "active": true},
{"name": "6th Floor", "sort_order": 7, "active": true}
]
}Office Building (4 floors):
{
"version": "1.0.0",
"floors": [
{"name": "Level 1", "sort_order": 1, "active": true},
{"name": "Level 2", "sort_order": 2, "active": true},
{"name": "Level 3", "sort_order": 3, "active": true},
{"name": "Level 4", "sort_order": 4, "active": true}
]
}Single Floor (1 floor):
{
"version": "1.0.0",
"floors": [
{"name": "Main", "sort_order": 0, "active": true}
]
}Defines departments and assigns them to floors and staff.
{
"version": "1.0.0",
"super_admin": {
"username": "admin",
"email": "admin@organization.local",
"password": "securepassword",
"full_name": "Administrator"
},
"departments": [
{
"name": "Department Name",
"description": "Brief description",
"floor": "Ground Floor",
"user": {
"username": "dept.username",
"email": "user@organization.local",
"password": "password123",
"full_name": "User Full Name"
}
},
{
"name": "Internal",
"description": "Admin-only department",
"floor": "Ground Floor",
"user": null
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
| username | string | Yes | Admin username (3-50 alphanumeric, dots, underscores, hyphens) |
| string | Yes | Admin email address | |
| password | string | Yes | Admin password (min 6 characters) |
| full_name | string | Yes | Admin full name (2-100 characters) |
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Department name (2-100 characters, unique) |
| description | string | No | Brief description of department |
| floor | string | Yes | Floor name (must exist in floors.json) |
| user | object/null | Yes | Department user or null for system departments |
| Field | Type | Required | Description |
|---|---|---|---|
| username | string | Yes | Username (3-50 alphanumeric, dots, underscores, hyphens) |
| string | Yes | Email address (unique across system) | |
| password | string | Yes | Password (min 6 characters) |
| full_name | string | Yes | Full name (2-100 characters) |
Department Name:
- Length: 2-100 characters
- Unique: No duplicate names
- Required: Cannot be empty
- Type: Must be a string
Floor Reference:
- Must exist in floors.json
- Case-sensitive match
- Required: Cannot be null/empty
- Type: Must be a string
User Requirements:
- Required departments: Must have a user object (except Internal)
- Internal only: Can have null user
- Cannot create multiple Internals: Only one "Internal" department allowed
Username Validation:
- Length: 3-50 characters
- Format: Alphanumeric, dots (.), underscores (_), hyphens (-)
- Unique: No duplicate usernames
- Examples:
john.doe,jane_smith,user-123
Email Validation:
- Format: Must contain @ and domain
- Unique: No duplicate emails
- Length: Max 100 characters
- Examples:
john@organization.local,jane@company.com
Password Validation:
- Length: Minimum 6 characters (longer recommended for production)
- Type: Must be a string
- Note: Stored securely (hashed with bcrypt)
Hospital Organization (10 departments):
{
"version": "1.0.0",
"super_admin": {
"username": "admin",
"email": "admin@hospital.local",
"password": "securepass123",
"full_name": "Administrator"
},
"departments": [
{
"name": "Emergency Department",
"description": "Emergency and urgent care services",
"floor": "Ground Floor",
"user": {
"username": "ed.coordinator",
"email": "ed.coordinator@hospital.local",
"password": "password123",
"full_name": "Dr. Sarah Martinez"
}
},
{
"name": "Cardiology",
"description": "Cardiovascular services",
"floor": "2nd Floor",
"user": {
"username": "cardiology.lead",
"email": "cardiology@hospital.local",
"password": "password123",
"full_name": "Dr. James Wilson"
}
},
{
"name": "Internal",
"description": "Admin-only internal work",
"floor": "Ground Floor",
"user": null
}
]
}Small Office (5 departments):
{
"version": "1.0.0",
"super_admin": {
"username": "admin",
"email": "admin@company.local",
"password": "companypass123",
"full_name": "Office Administrator"
},
"departments": [
{
"name": "Human Resources",
"description": "HR services",
"floor": "Level 1",
"user": {
"username": "hr.manager",
"email": "hr@company.local",
"password": "password123",
"full_name": "Alice Johnson"
}
},
{
"name": "Finance",
"description": "Financial operations",
"floor": "Level 2",
"user": {
"username": "finance.lead",
"email": "finance@company.local",
"password": "password123",
"full_name": "Bob Smith"
}
},
{
"name": "IT Support",
"description": "Technical support",
"floor": "Level 1",
"user": {
"username": "it.support",
"email": "it@company.local",
"password": "password123",
"full_name": "Charlie Brown"
}
},
{
"name": "Internal",
"description": "Admin-only",
"floor": "Level 1",
"user": null
}
]
}# Load configurations and validate before seeding
npm run seed:hospital- Load JSON config files
- Validate all configurations (50+ validation rules)
- Display errors if any issues found (does NOT modify database)
- Seed database if all validations pass
- Log audit trail of all created records
Validation Error - Duplicate Floor:
❌ Configuration validation failed:
- Floor[2]: Duplicate floor name "Ground Floor"
Please fix the errors above and try again.
Validation Error - Invalid Floor Reference:
❌ Configuration validation failed:
- Department[3].floor: Floor "10th Floor" does not exist in floors configuration
Please fix the errors above and try again.
Validation Error - Invalid Email:
❌ Configuration validation failed:
- Department[1].user.email: Invalid email format "invalid-email"
Please fix the errors above and try again.
| Issue | Cause | Fix |
|---|---|---|
| "Floor does not exist" | Department references floor name not in floors.json | Check floor name spelling (case-sensitive) |
| "Duplicate floor name" | Two floors with same name | Use unique floor names |
| "Invalid email format" | Email missing @ or domain | Use valid email: user@domain.local |
| "Duplicate username" | Two users with same username | Change one username to be unique |
| "Length must be between X and Y" | Field too short or too long | Adjust field to fit constraint |
| "Missing Internal department" | Internal not defined in departments | Add "Internal" department with user: null |
| "Multiple Internal departments" | More than one Internal defined | Keep only one "Internal" department |
Mark floors as inactive instead of removing them:
{
"name": "Old Floor",
"sort_order": 10,
"active": false
}Effect:
- Floor exists in database but hidden from UI dropdowns
- Department queries exclude inactive floors
- Allows keeping historical data without confusion
You can add more floors after initial seeding:
- Edit
config/seed-data/floors.jsonto add new floor - Run
npm run seed:hospitalagain - Seeder skips existing floors, adds new ones
To rename a department after seeding:
- Use Admin UI → Departments → Edit
- Change department name
- All tickets automatically update (FK CASCADE)
- Department users' department field updates automatically
For production deployments, use secure passwords:
Do NOT:
- Hardcode passwords in JSON files
- Use simple passwords like "password123"
- Commit JSON files with production passwords
Instead:
- Use environment variables
- Use secret management tools (AWS Secrets Manager, HashiCorp Vault, etc.)
- Load passwords at runtime
- Use complex passwords (16+ characters)
Adapt floor names for your organization:
Hospital: Basement, Ground Floor, 1st-6th Floor Office: Level 1, Level 2, Level 3 Factory: Building A-Floor 1, Building B-Floor 2 University: Basement, Ground Floor, Tower 1, Tower 2
Choose names that match your physical layout.
The seeder validates before making any database changes. Simply run:
npm run seed:hospitalIf there are errors, fix them and run again. No data is modified until validation passes.
# Reset database for clean testing
docker-compose down -v
docker-compose up -d
# Initialize database
npm run seed:hospital
# Verify in admin UI
# Visit http://localhost:3000/admin/floors
# Visit http://localhost:3000/admin/departmentsCheck floors:
docker-compose exec db psql -U ticketing_user -d ticketing_db \
-c "SELECT name, sort_order, is_system FROM floors ORDER BY sort_order;"Check departments:
docker-compose exec db psql -U ticketing_user -d ticketing_db \
-c "SELECT name, floor, is_system FROM departments ORDER BY name;"Check users:
docker-compose exec db psql -U ticketing_user -d ticketing_db \
-c "SELECT username, email, role, department FROM users;"Symptom: Database setup fails during docker-compose up
Solution:
- Reset database:
docker-compose down -v - Rebuild containers:
docker-compose up -d - Check logs:
docker-compose logs db
Symptom: Error when running npm run seed:hospital
Check:
- Verify floor names in departments.json match floors.json exactly (case-sensitive)
- Example: "Ground Floor" ≠ "ground floor" ≠ "GroundFloor"
Fix:
// ❌ WRONG - Different capitalization
"floor": "ground floor"
// ✅ CORRECT - Matches floors.json exactly
"floor": "Ground Floor"Symptom: "Duplicate username" validation error
Check:
- Each department user must have unique username
- Super admin username must be unique
Fix:
// ❌ WRONG - Same username
{"name": "Dept1", "user": {"username": "user1", ...}},
{"name": "Dept2", "user": {"username": "user1", ...}},
// ✅ CORRECT - Unique usernames
{"name": "Dept1", "user": {"username": "dept1.user", ...}},
{"name": "Dept2", "user": {"username": "dept2.user", ...}},Symptom: "Invalid email format" error
Valid Emails:
Invalid Emails:
- user (no @)
- user@ (no domain)
- user@domain (no TLD)
- user name@domain.com (space in username)
- Minimum: 6 characters (enforced)
- Recommended: 12+ characters for production
- Never: Share passwords in version control
- Consider: Using environment variables for production passwords
- Version Control: Only commit examples, not actual configs with real passwords
- File Permissions: Restrict access to config/seed-data/ directory
- Backups: Keep backups of configs separately from code
- Test First: Always test in development before production
- Review Output: Check seeder output for unexpected errors
- Audit Log: All created records are logged in audit_logs table
- Use secret management (AWS Secrets Manager, Vault, etc.)
- Never hardcode passwords in JSON
- Use environment variables for sensitive data
- Rotate passwords regularly
Q: Can I modify floors/departments after seeding? A: Yes, use the Admin UI (Floors and Departments sections) to create, edit, and manage them after seeding.
Q: What happens if I run the seeder twice? A: It's safe! The seeder is idempotent - it skips existing floors/departments and creates new ones only.
Q: Can I delete a floor? A: No, if departments reference it. Change departments to different floors first, then delete via Admin UI.
Q: Can I rename a floor? A: Yes, via Admin UI. All department references update automatically (FK CASCADE).
Q: Is the Internal department special?
A: Yes, it's marked as is_system=true and protected from deletion. It's for admin-only tickets.
Q: Can I have multiple organizations? A: Not by default. This system is single-organization per database.
Q: Where do custom fields go?
A: Extend the Department model in models/Department.js and add migrations.
- v2.2.1 (Jan 2026) - Department accounts, dual-portal, and floor management feature
- v2.2.0 - Initial dual-portal architecture for department accounts
- v2.1.0 - Department user role and client portal access
For issues or questions:
- Check this guide and error messages
- Review validator output for specific field errors
- Verify config file JSON syntax (use JSON validator)
- Check Floor and Department tables in Admin UI
- Review database logs:
docker-compose logs db