Admin UI Guide
Multi-tenant clinic management with RBAC authentication
Version: 1.5.1
Updated: 2026-01-16
Overview
The Vitara Admin UI provides a React-based web interface for managing clinic configurations, EMR integrations, user accounts, and audit logs. It supports two distinct user roles with separate dashboards.
URL: https://{host}/admin/
User Roles
| Role |
Access |
Description |
| Vitara Admin |
All clinics |
Platform administrators with full system access |
| Clinic Manager |
Own clinic |
Single-clinic administrators for self-service |
Authentication
JWT-Based Authentication
The admin system uses PassportJS + JWT for authentication:
- Access tokens (1 hour expiry)
- Refresh tokens (7 days expiry)
- bcrypt password hashing (cost factor 12)
- Account lockout after 5 failed attempts (15 min)
Login Flow
- User submits email/password to
/api/auth/login
- Server validates credentials against
admin_users table
- Returns JWT access token + refresh token
- Frontend stores tokens in localStorage
- All API requests include
Authorization: Bearer {token}
Password Requirements
- Minimum 8 characters
- At least one letter and one number
- Checked against common passwords
Vitara Admin Features
Dashboard (/admin/dashboard)
- Clinic Statistics: Total, active, live, pending approval counts
- Recent Activity: Last 24 hours audit log summary
- Quick Actions: Links to clinic/user management
Clinic Management (/admin/clinics)
| Feature |
Description |
| List Clinics |
Paginated table with search and status filter |
| Create Clinic |
Add new clinic with name, slug, timezone |
| View/Edit Clinic |
Update clinic details, contact info, status |
| EMR Configuration |
Configure OSCAR connection (URL, API key, OAuth) |
| Preferences |
Language, booking rules, reminders |
| Go-Live Workflow |
Validate and enable EMR integration |
User Management (/admin/users)
| Feature |
Description |
| List Users |
Paginated table with role filter |
| Create User |
Add vitara_admin or clinic_manager |
| Edit User |
Update name, email, role, clinic assignment |
| Reset Password |
Admin-initiated password reset |
| Unlock Account |
Clear failed login attempts |
| Activate/Deactivate |
Enable or disable user accounts |
Audit Logs (/admin/audit-logs)
| Feature |
Description |
| Query Logs |
Filter by action, user, date range |
| View Details |
See changes and metadata per entry |
| Activity Summary |
Dashboard widget with counts by action |
Clinic Manager Features
My Clinic (/admin/my-clinic)
| Feature |
Description |
| Clinic Info |
View clinic name, timezone, contact details |
| Edit Preferences |
Update booking rules, reminder hours |
| Request Go-Live |
Submit request for EMR activation |
Limitations
Clinic Managers cannot:
- View or manage other clinics
- Create or manage users
- View system-wide audit logs
- Directly enable EMR live status
Go-Live Workflow
The go-live workflow ensures clinics are properly configured before enabling real EMR operations.
Validation Checks
| Check |
Requirement |
| Status |
Clinic must be active or pending_approval |
| EMR URL |
OSCAR base URL must be configured |
| API Key |
EMR API key must be set |
| Provider |
Default provider number must be configured |
Process
- Vitara Admin: Configure EMR settings for clinic
- Clinic Manager: Test in demo mode, request go-live
- Vitara Admin: Run validation checks
- Vitara Admin: Enable
emr_live if all checks pass
- System: Voice agent now uses real EMR for this clinic
Demo Mode
When emr_live = false:
- Voice agent returns mock/demo data
- No real EMR operations performed
- Clinic can test voice agent safely
- Demo patient: "Demo Patient" (DEMO-001)
- Demo provider: "Dr. Demo Doctor" (DEMO-DR-001)
Standard Response Structure
All admin API endpoints return responses in a consistent format:
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 25,
"totalPages": 3
}
}
The frontend api.js service unwraps this structure:
- For paginated endpoints: Returns { data: [...], pagination: {...} }
- For non-paginated endpoints: Returns just the data array/object
Database Field Naming
The API returns PostgreSQL column names directly. Frontend components use these field names:
| Table |
Field |
Description |
clinic_config |
clinic_id |
UUID primary key |
clinic_config |
clinic_name |
Clinic display name |
clinic_config |
clinic_created_at |
Creation timestamp |
clinic_config |
emr_live |
EMR live status boolean |
admin_users |
id |
UUID primary key |
admin_users |
email |
User email |
admin_users |
role |
vitara_admin or clinic_manager |
audit_log |
changes |
JSONB - before/after values |
audit_log |
metadata |
JSONB - additional context |
JSONB Field Handling
PostgreSQL JSONB columns are auto-parsed by the pg driver. The backend uses safeJsonParse() to handle both pre-parsed objects and string values:
function safeJsonParse(value) {
if (value === null || value === undefined) return null;
if (typeof value === 'object') return value; // Already parsed
try {
return JSON.parse(value);
} catch {
return value;
}
}
API Endpoints
Authentication
| Method |
Endpoint |
Description |
| POST |
/api/auth/login |
User login |
| POST |
/api/auth/logout |
User logout |
| POST |
/api/auth/refresh |
Refresh access token |
| POST |
/api/auth/change-password |
Change own password |
| GET |
/api/auth/me |
Get current user info |
Admin - Clinics (Vitara Admin only)
| Method |
Endpoint |
Description |
| GET |
/api/admin/clinics |
List clinics (paginated) |
| GET |
/api/admin/clinics/stats |
Get clinic statistics |
| POST |
/api/admin/clinics |
Create new clinic |
| GET |
/api/admin/clinics/:id |
Get clinic details |
| PUT |
/api/admin/clinics/:id |
Update clinic |
| DELETE |
/api/admin/clinics/:id |
Delete clinic |
| PUT |
/api/admin/clinics/:id/emr-config |
Update EMR configuration |
| POST |
/api/admin/clinics/:id/test-connection |
Test EMR connection |
| GET |
/api/admin/clinics/:id/validate-go-live |
Validate go-live requirements |
| POST |
/api/admin/clinics/:id/go-live |
Set EMR live status |
Admin - Users (Vitara Admin only)
| Method |
Endpoint |
Description |
| GET |
/api/admin/users |
List users (paginated) |
| POST |
/api/admin/users |
Create user |
| GET |
/api/admin/users/:id |
Get user details |
| PUT |
/api/admin/users/:id |
Update user |
| DELETE |
/api/admin/users/:id |
Delete user |
| POST |
/api/admin/users/:id/reset-password |
Reset user password |
| POST |
/api/admin/users/:id/unlock |
Unlock user account |
| POST |
/api/admin/users/:id/activate |
Activate user |
| POST |
/api/admin/users/:id/deactivate |
Deactivate user |
Admin - Audit Logs (Vitara Admin only)
| Method |
Endpoint |
Description |
| GET |
/api/admin/audit-logs |
List audit logs (paginated) |
| GET |
/api/admin/audit-logs/summary |
Get activity summary |
Clinic Manager - Self-Service
| Method |
Endpoint |
Description |
| GET |
/api/clinic/me |
Get own clinic |
| PUT |
/api/clinic/preferences |
Update clinic preferences |
| POST |
/api/clinic/request-go-live |
Request go-live status |
Database Tables
admin_users
| Column |
Type |
Description |
| id |
UUID |
Primary key |
| email |
VARCHAR(255) |
Unique, lowercase |
| password_hash |
VARCHAR(255) |
bcrypt hash |
| role |
VARCHAR(50) |
vitara_admin or clinic_manager |
| clinic_id |
UUID |
FK to clinic_config (for managers) |
| first_name |
VARCHAR(100) |
Optional |
| last_name |
VARCHAR(100) |
Optional |
| is_active |
BOOLEAN |
Account status |
| failed_login_attempts |
INTEGER |
Lockout counter |
| locked_until |
TIMESTAMP |
Lockout expiry |
audit_log
| Column |
Type |
Description |
| id |
UUID |
Primary key |
| action |
VARCHAR(100) |
e.g., auth.login, clinic.update |
| resource_type |
VARCHAR(50) |
e.g., clinic_config, admin_user |
| resource_id |
UUID |
Affected resource ID |
| user_id |
UUID |
Acting user |
| user_email |
VARCHAR(255) |
For display |
| user_role |
VARCHAR(50) |
Role at time of action |
| changes |
JSONB |
Before/after values |
| metadata |
JSONB |
Additional context |
| ip_address |
INET |
Request IP |
| created_at |
TIMESTAMP |
Immutable |
Configuration
Environment Variables
| Variable |
Default |
Description |
JWT_SECRET |
(required) |
Access token signing key |
JWT_REFRESH_SECRET |
(required) |
Refresh token signing key |
JWT_EXPIRES_IN |
1h |
Access token expiry |
JWT_REFRESH_EXPIRES_IN |
7d |
Refresh token expiry |
EMR_DEMO_MODE |
true |
Enable demo responses |
Seeding Initial Admin
-- Create first Vitara Admin (password: changeme123)
INSERT INTO admin_users (
id, email, password_hash, role, is_active
) VALUES (
gen_random_uuid(),
'admin@vitara.io',
'$2a$12$...(bcrypt hash)...',
'vitara_admin',
true
);
Security Features
| Feature |
Implementation |
| Password Hashing |
bcrypt with cost factor 12 |
| JWT Tokens |
HS256 algorithm, short-lived |
| Account Lockout |
5 failed attempts = 15 min lock |
| Audit Logging |
All admin actions logged |
| RBAC |
Role-based access control middleware |
| Input Validation |
Joi schemas on all endpoints |
Tech Stack
| Component |
Technology |
| Frontend |
React 18, React Router, Tailwind CSS |
| Build |
Vite |
| Backend |
Express.js |
| Auth |
PassportJS, JWT |
| Database |
PostgreSQL |
| Validation |
Joi |
Support
For admin UI issues, contact support@vitaravox.com