Skip to main content

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:

  1. Role Management: Create/update roles → Permission validation → Inheritance calculation → User assignment
  2. Feature Flags: Configure flags → Dependency checking → A/B testing setup → Gradual rollout
  3. Branding: Upload assets → Validation → Optimization → CDN distribution → Theme application

GraphQL Operations with Business Rules:

ActionOperationBusiness Rules
Update RoleupdateRole(input)Permission hierarchy, inheritance, context-aware access
Set FlagsetFeatureFlag(input)A/B testing, user segmentation, dependency management
Save BrandingsaveBranding(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