1. Overview

The data visibility feature within the access control page allows administrators to control what data different user roles can access based on various rules. The implementation ensures that each role has exactly one rule applied to prevent conflicts or gaps in data access permissions. The interface presents these permissions in a tabular format with checkbox controls for different user roles, enabling administrators to configure precise data visibility rules across the organization.

Integration Points:

  • Settings - Parent settings management system
  • Access Control - Permission management framework
  • Users - User role assignment and management
  • Sites - Location-based data visibility
  • Departments - Department-level access control

2. Libraries and Dependencies

LibraryPurpose
ReactComponent-based UI development and state management
styled-componentsComponent styling with CSS-in-JS approach
clsxConditional class name construction
react-i18nextInternationalization of UI text
react-toastifyDisplaying toast notifications for validation errors
@nimbly-technologies/nimbly-commonCommon types shared across the Nimbly platform
ReduxState management (accessed via useSelector)

3. Implementation Structure

3.1 Main Component: PermissionsContainer

The PermissionsContainer is the primary component responsible for rendering the access control interface, including the data visibility tab.

Key Props:

  • isNimbly - Boolean flag to determine if this is a Nimbly instance
  • isFetching/isBusy - Loading states for the component
  • permissions - Restructured resource data that includes data visibility rules
  • selectedTab - The currently selected tab in the interface
  • userRoles - List of user roles available in the system
  • access - Current user’s permission values
  • accessLevel - Current user’s role level
  • onChangeValue - Handler for permission value changes
  • onSave - Handler to persist changes to the database
  • onReset - Handler to reset permissions to default values

3.2 Tab Navigation

The data visibility feature is accessed through a dedicated tab in the permission interface. The tab structure is defined in the TabsNav component:

{
  label: 'data Visibility',
  value: 'data-visibility',
  text: 'label.settingPage.permissions.data-Visibility',
}

3.3 Table Structure

The permission interface uses a table structure with:

  1. Header row: Shows user roles as column headers
  2. Feature rows: Groups permissions by features
  3. Permission rows: Individual permission items with checkboxes for each role

4. Data Visibility Rules and Validation

4.1 Rule Structure

Data visibility permissions are structured as follows:

{
  'data-visibility': {
    rules: {
      all: {
        permissions: {
          view: {
            admin: boolean,
            manager: boolean,
            // other roles...
          }
        }
      },
      'specific-dept': {
        permissions: {
          view: {
            admin: boolean,
            manager: boolean,
            // other roles...
          }
        }
      },
      // other rules...
    }
  }
}

4.2 Validation Logic

A key aspect of the data visibility implementation is the validation logic that ensures each role has exactly one rule applied:

const save = () => {
  const dataVisibilityPermissions = props.permissions["data-visibility"];
  let isError = false;
 
  if (dataVisibilityPermissions) {
    // Gets an array of all the roles
    const roles = Object.keys(
      dataVisibilityPermissions.rules.all.permissions.view ?? {}
    );
 
    // Keeps count of the number of rules selected for each role
    let roleCount: Record<string, number> = {};
 
    // Number of rules selected initialized to zero
    roles.forEach((item) => {
      roleCount[item] = 0;
    });
 
    // Iterate through each rule
    Object.entries(dataVisibilityPermissions.rules).forEach(
      ([rule, permissions]) => {
        // Iterate through permissions for each rule
        Object.entries(permissions.permissions).forEach(
          ([permission, view]) => {
            Object.entries(roleCount).forEach(([role, count]) => {
              roleCount[role] = view?.[role]
                ? roleCount[role] + 1
                : roleCount[role];
            });
          }
        );
      }
    );
 
    // Validate that each role has exactly one rule
    Object.entries(roleCount).forEach(([role, count]) => {
      if (count > 1) {
        const roleObj = userRoles.find((item) => item.value === role);
        toast.error(
          `Error: Multiple permissions selected for '${roleObj?.label ?? ""}'`
        );
        isError = true;
      } else if (count === 0) {
        const roleObj = userRoles.find((item) => item.value === role);
        toast.error(
          `Error: No permission selected for '${roleObj?.label ?? ""}'`
        );
        isError = true;
      }
    });
  }
 
  // Only save if validation passes
  if (!isError) {
    handleSave();
  }
};

Validation ensures:

  • No role has multiple conflicting data visibility rules
  • Every role has at least one data visibility rule assigned

5. UI Components and Styling

5.1 Table Components

ComponentPurpose
SettingPermissionTableMain table component with sticky headers and columns
TableWrapperContainer for the table with overflow handling
ColumnTable cell component with conditional cursor styling based on permission status

5.2 Permission Controls

  • Checkbox: Custom checkbox component used for toggling permissions
  • handleChangeValue: Function that updates permission values when checkboxes are clicked

5.3 Responsive Design

The interface includes responsive design elements:

  • Desktop view: Full table layout with horizontal scrolling for many columns
  • Mobile view: Placeholder for mobile-specific layout (marked as TODO in the code)
  • FloatingButton: Fixed-position save button for mobile screens

6. State Management and Data Flow

6.1 Permission State Structure

The permissions data is structured as a nested object with specific paths for each permission:

[tabName, feature, access, 'permissions', permission, role]

Example:

['data-visibility', 'rules', 'all', 'permissions', 'view', 'admin']

6.2 State Updates

When a checkbox is clicked:

  1. handleChangeValue is called with the path and new value
  2. This triggers the onChangeValue prop function, updating the permission state
  3. Changes remain in memory until saved

6.3 Saving Changes

The save process involves:

  1. Validating the current permission state
  2. If valid, calling handleSave which triggers the onSave prop
  3. The parent component then persists changes to the database

7. File Paths and Code References

FilePath
Main Componentsrc/components/permissions/PermissionsContainer.tsx
Checkbox Componentsrc/components/permissions/components/Checkbox.tsx
Reset Modalsrc/components/permissions/components/PermissionsResetConfirmationModal.tsx
Typessrc/components/permissions/typings.ts

8. Summary

The data visibility feature in the access control page provides a structured, rule-based approach to managing what data different roles can access within the organization.

Key aspects include:

  • Rule-based permissions: Different visibility rules (all, specific-dept, specific-site, etc.)
  • Role-specific controls: Permissions