Settings
Settings provides organization-wide configuration: roles & permissions, feature flags, branding, and integrations. This page documents the Web/Admin operations with critical business logic for access control, feature management, and system configuration.
Core Concepts
- Role-Based Access Control (RBAC): Hierarchical roles with granular permissions and inheritance
- Feature Flags: Environment-specific feature toggles with A/B testing capabilities
- Branding Management: Custom logos, colors, and theme configuration
- Integration Settings: Third-party service configurations and API management
- Audit Logging: Complete audit trail for all configuration changes
- Environment Management: Separate configurations for development, staging, and production
Web/Admin Capabilities
1) Roles & Permissions (GraphQL)
RBAC Business Rules:
- Role Hierarchy: Super Admin > Admin > Manager > User with permission inheritance
- Permission Granularity: Module-level and action-level permissions (read, write, delete)
- Dynamic Permissions: Context-aware permissions based on data ownership
- Permission Validation: Real-time permission checking for all operations
- Role Assignment: Multi-role support with permission aggregation
Permission System Logic:
// Define permission hierarchy and inheritance
const PERMISSION_HIERARCHY = {
SUPER_ADMIN: ['*'], // All permissions
ADMIN: [
'users.*',
'appointments.*',
'assessments.*',
'lesson_plans.*',
'transactions.read',
'subscriptions.*',
'settings.*',
],
MANAGER: [
'appointments.read',
'appointments.write',
'assessments.read',
'lesson_plans.read',
'lesson_plans.write',
'users.read',
],
THERAPIST: [
'appointments.read',
'appointments.write',
'assessments.read',
'assessments.write',
'lesson_plans.read',
'lesson_plans.write',
],
PARENT: ['appointments.read', 'assessments.read', 'lesson_plans.read'],
};
// Check if user has specific permission
const hasPermission = (user, permission, context = {}) => {
const userRoles = user.roles || [];
// Check direct role permissions
for (const role of userRoles) {
const rolePermissions = PERMISSION_HIERARCHY[role] || [];
// Check for wildcard permissions
if (rolePermissions.includes('*')) {
return true;
}
// Check for exact permission match
if (rolePermissions.includes(permission)) {
return true;
}
// Check for module-level permissions (e.g., 'appointments.*')
const module = permission.split('.')[0];
if (rolePermissions.includes(`${module}.*`)) {
return true;
}
}
// Context-aware permission checking
if (context.ownershipCheck) {
return checkOwnershipPermission(user, permission, context);
}
return false;
};
// Context-aware permission checking (e.g., users can only edit their own data)
const checkOwnershipPermission = (user, permission, context) => {
if (permission.includes('write') || permission.includes('delete')) {
// Check if user owns the resource
if (context.resourceOwnerId === user.id) {
return true;
}
// Check if user is admin/manager (can edit others' data)
return user.roles.some((role) => ['ADMIN', 'MANAGER'].includes(role));
}
return false;
};
GraphQL Implementation:
import { gql, ApolloClient } from '@apollo/client';
const UPDATE_ROLE = gql`
mutation UpdateRole($input: UpdateRoleInput!) {
updateRole(input: $input) {
success
message
data {
id
name
permissions
inherited_permissions
permission_count
users_count
last_updated
}
}
}
`;
async function updateRole(
client: ApolloClient<unknown>,
input: UpdateRoleInput,
currentUser: User,
) {
// Validate current user has permission to update roles
if (!hasPermission(currentUser, 'settings.roles.write')) {
throw new Error('Insufficient permissions to update roles');
}
// Validate permission changes
const validationErrors = validateRolePermissions(input.permissions);
if (validationErrors.length > 0) {
throw new Error(
`Permission validation failed: ${validationErrors.join(', ')}`,
);
}
// Calculate inherited permissions
const inheritedPermissions = calculateInheritedPermissions(input.permissions);
const processedInput = {
...input,
inherited_permissions: inheritedPermissions,
updated_by: currentUser.id,
last_updated: new Date().toISOString(),
};
const { data } = await client.mutate({
mutation: UPDATE_ROLE,
variables: { input: processedInput },
});
if (!data?.updateRole?.success) {
throw new Error(data?.updateRole?.message || 'Update role failed');
}
return data.updateRole.data;
}
2) Feature Flags (GraphQL)
Feature Flag Business Rules:
- Environment-Specific: Different flag values for dev, staging, and production
- A/B Testing: Percentage-based rollouts with user segmentation
- Gradual Rollout: Phased feature releases with monitoring and rollback capabilities
- User Segmentation: Target specific user groups or subscription tiers
- Dependency Management: Handle feature flag dependencies and conflicts
Feature Flag Logic:
// Feature flag evaluation with A/B testing
const evaluateFeatureFlag = (flagKey, user, context = {}) => {
const flag = getFeatureFlag(flagKey);
if (!flag || !flag.isActive) {
return false;
}
// Environment check
if (flag.environment !== context.environment) {
return false;
}
// User segmentation
if (flag.userSegments && flag.userSegments.length > 0) {
if (!isUserInSegment(user, flag.userSegments)) {
return false;
}
}
// A/B testing percentage
if (flag.rolloutPercentage < 100) {
const userHash = hashUserId(user.id, flagKey);
const userPercentage = (userHash % 100) + 1;
if (userPercentage > flag.rolloutPercentage) {
return false;
}
}
// Subscription tier check
if (
flag.requiredSubscriptionTiers &&
flag.requiredSubscriptionTiers.length > 0
) {
const userSubscription = getUserSubscription(user.id);
if (!flag.requiredSubscriptionTiers.includes(userSubscription.tier)) {
return false;
}
}
return true;
};
// User segmentation logic
const isUserInSegment = (user, segments) => {
return segments.some((segment) => {
switch (segment.type) {
case 'SUBSCRIPTION_TIER':
const userSubscription = getUserSubscription(user.id);
return segment.values.includes(userSubscription.tier);
case 'USER_ROLE':
return segment.values.some((role) => user.roles.includes(role));
case 'REGISTRATION_DATE':
const registrationDate = new Date(user.created_at);
const segmentDate = new Date(segment.value);
return registrationDate >= segmentDate;
case 'CUSTOM_ATTRIBUTE':
return user.customAttributes?.[segment.attribute] === segment.value;
default:
return false;
}
});
};
// Feature flag dependency checking
const checkFeatureDependencies = (flag) => {
if (!flag.dependencies || flag.dependencies.length === 0) {
return { valid: true, errors: [] };
}
const errors = [];
for (const dependency of flag.dependencies) {
const dependencyFlag = getFeatureFlag(dependency.flagKey);
if (!dependencyFlag) {
errors.push(`Dependency flag '${dependency.flagKey}' not found`);
continue;
}
if (dependency.required && !dependencyFlag.isActive) {
errors.push(`Required dependency '${dependency.flagKey}' is not active`);
}
}
return {
valid: errors.length === 0,
errors,
};
};
GraphQL Implementation:
const SET_FLAG = gql`
mutation SetFlag($input: SetFeatureFlagInput!) {
setFeatureFlag(input: $input) {
success
message
data {
key
value
environment
rollout_percentage
user_segments
dependencies
last_updated
updated_by
}
}
}
`;
async function setFlag(
client: ApolloClient<unknown>,
input: SetFeatureFlagInput,
currentUser: User,
) {
// Validate current user has permission to manage feature flags
if (!hasPermission(currentUser, 'settings.feature_flags.write')) {
throw new Error('Insufficient permissions to manage feature flags');
}
// Check feature flag dependencies
const dependencyCheck = checkFeatureDependencies(input);
if (!dependencyCheck.valid) {
throw new Error(
`Feature flag dependencies not met: ${dependencyCheck.errors.join(', ')}`,
);
}
// Validate rollout percentage
if (input.rolloutPercentage < 0 || input.rolloutPercentage > 100) {
throw new Error('Rollout percentage must be between 0 and 100');
}
const processedInput = {
...input,
updated_by: currentUser.id,
last_updated: new Date().toISOString(),
};
const { data } = await client.mutate({
mutation: SET_FLAG,
variables: { input: processedInput },
});
if (!data?.setFeatureFlag?.success) {
throw new Error(data?.setFeatureFlag?.message || 'Set flag failed');
}
return data.setFeatureFlag.data;
}
3) Branding & Theme Management (GraphQL)
Branding Business Rules:
- Asset Validation: Validate logo dimensions, file size, and format requirements
- Color Validation: Ensure color contrast ratios meet accessibility standards
- Theme Consistency: Maintain consistent branding across all platform interfaces
- Asset Optimization: Automatic image compression and format optimization
- CDN Integration: Automatic asset distribution to CDN for performance
Branding Validation Logic:
// Validate branding assets and colors
const validateBranding = (branding) => {
const errors = [];
// Logo validation
if (branding.logo) {
const logoValidation = validateLogo(branding.logo);
if (!logoValidation.valid) {
errors.push(...logoValidation.errors);
}
}
// Color validation
if (branding.primaryColor) {
const colorValidation = validateColor(branding.primaryColor);
if (!colorValidation.valid) {
errors.push(...colorValidation.errors);
}
}
// Theme validation
if (branding.theme) {
const themeValidation = validateTheme(branding.theme);
if (!themeValidation.valid) {
errors.push(...themeValidation.errors);
}
}
return {
valid: errors.length === 0,
errors,
};
};
// Logo validation
const validateLogo = (logo) => {
const errors = [];
// File size validation (max 2MB)
if (logo.size > 2 * 1024 * 1024) {
errors.push('Logo file size must be less than 2MB');
}
// Format validation
const allowedFormats = ['image/png', 'image/jpeg', 'image/svg+xml'];
if (!allowedFormats.includes(logo.type)) {
errors.push('Logo must be PNG, JPEG, or SVG format');
}
// Dimension validation (for raster images)
if (logo.type !== 'image/svg+xml') {
if (logo.width < 200 || logo.height < 200) {
errors.push('Logo must be at least 200x200 pixels');
}
if (logo.width > 1000 || logo.height > 1000) {
errors.push('Logo must be less than 1000x1000 pixels');
}
}
return {
valid: errors.length === 0,
errors,
};
};
// Color validation with accessibility checks
const validateColor = (color) => {
const errors = [];
// Color format validation
if (!isValidColorFormat(color)) {
errors.push('Invalid color format. Use hex (#RRGGBB) or RGB format');
return { valid: false, errors };
}
// Contrast ratio validation
const contrastRatio = calculateContrastRatio(color, '#FFFFFF');
if (contrastRatio < 4.5) {
errors.push(
'Color contrast ratio must be at least 4.5:1 for accessibility',
);
}
return {
valid: errors.length === 0,
errors,
};
};
// Theme validation
const validateTheme = (theme) => {
const errors = [];
// Required theme properties
const requiredProperties = [
'primaryColor',
'secondaryColor',
'backgroundColor',
'textColor',
];
for (const prop of requiredProperties) {
if (!theme[prop]) {
errors.push(`Theme must include ${prop}`);
}
}
// Color harmony validation
if (theme.primaryColor && theme.secondaryColor) {
const harmony = calculateColorHarmony(
theme.primaryColor,
theme.secondaryColor,
);
if (harmony < 0.3) {
errors.push('Primary and secondary colors must have sufficient contrast');
}
}
return {
valid: errors.length === 0,
errors,
};
};
GraphQL Implementation:
const SAVE_BRANDING = gql`
mutation SaveBranding($input: SaveBrandingInput!) {
saveBranding(input: $input) {
success
message
data {
logo_url
primary_color
secondary_color
background_color
text_color
theme_config
cdn_urls {
logo_url
favicon_url
}
last_updated
updated_by
}
}
}
`;
async function saveBranding(
client: ApolloClient<unknown>,
input: SaveBrandingInput,
currentUser: User,
) {
// Validate current user has permission to manage branding
if (!hasPermission(currentUser, 'settings.branding.write')) {
throw new Error('Insufficient permissions to manage branding');
}
// Validate branding assets and colors
const validation = validateBranding(input);
if (!validation.valid) {
throw new Error(
`Branding validation failed: ${validation.errors.join(', ')}`,
);
}
// Process and optimize assets
const processedInput = await processBrandingAssets(input);
const { data } = await client.mutate({
mutation: SAVE_BRANDING,
variables: {
input: {
...processedInput,
updated_by: currentUser.id,
last_updated: new Date().toISOString(),
},
},
});
if (!data?.saveBranding?.success) {
throw new Error(data?.saveBranding?.message || 'Branding save failed');
}
return data.saveBranding.data;
}
Data Flow (Web ↔ Backend)
Critical Business Logic Flow:
- Role Management: Create/update roles → Permission validation → Inheritance calculation → User assignment
- Feature Flags: Configure flags → Dependency checking → A/B testing setup → Gradual rollout
- Branding: Upload assets → Validation → Optimization → CDN distribution → Theme application
GraphQL Operations with Business Rules:
| Action | Operation | Business Rules |
|---|---|---|
| Update Role | updateRole(input) | Permission hierarchy, inheritance, context-aware access |
| Set Flag | setFeatureFlag(input) | A/B testing, user segmentation, dependency management |
| Save Branding | saveBranding(input) | Asset validation, accessibility, CDN optimization |
Error Handling & Validation:
- Permission Denied: "Insufficient permissions to update roles"
- Invalid Permissions: "Permission validation failed"
- Feature Dependencies: "Feature flag dependencies not met"
- Asset Validation: "Logo file size must be less than 2MB"
- Color Accessibility: "Color contrast ratio must be at least 4.5:1"
Security & Access Control
- Super Admin Only: Critical system settings require super admin access
- Audit Trail: All configuration changes logged with user information and timestamps
- Environment Isolation: Separate configurations for different environments
- Data Privacy: Configuration data protected by role-based permissions
- Change Approval: Major configuration changes require approval workflow