npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@visns-studio/visns-components

v5.15.31

Published

Various packages to assist in the development of our Custom Applications.

Readme

VISNS Components

A comprehensive React component library used by the VISNS Studio team for CRM and CMS projects, featuring authentication, data visualization, form handling, and more.

npm version license

Overview

VISNS Components is a React-based UI component library that provides a set of reusable, consistent, and customizable components for building web applications. It includes components for authentication, data grids, forms, navigation, and more, designed to work seamlessly together.

Recent Updates (v5.15.10)

Latest Enhancements

  • Phone and ABN Formatting: Added specialized formatting for Australian phone numbers and ABNs across DataGrid, GenericDetail, and JSON fields with format support
  • CameraPlacement Legacy Import: Added comprehensive legacy JSON file import functionality with support for multiple data formats, automatic image loading, and seamless database integration
  • CameraPlacement Module: Enhanced interactive camera placement tool with improved UI consistency, better icon visibility, drag-and-drop image upload, professional export capabilities, and complete Verkada database integration
  • Complete StandardModal Migration: All components now use StandardModal instead of reactjs-popup for consistent modal behavior across the entire library
  • HTML Content Support: Terms and conditions now properly parse HTML content from TinyMCE editors with clean rendering
  • Improved Typography: Refined font sizing in Terms and Conditions to match page typography hierarchy
  • Modal Component Refactoring: StandardModal converted to pure React implementation with enhanced accessibility and mobile support
  • Enhanced JSON Table Field: Improved json-table field type with better styling, user experience, and data editing capabilities
  • Dynamic Action Labels: GenericDetail component now supports dynamic action labels and custom action buttons for better user interaction
  • Dependency Updates: Updated lucide-react to version 0.536.0 for improved icon consistency and performance
  • GenericAuth Enhancements: Additional config prop support for more flexible authentication configurations
  • GenericGrid Advanced Features: Enhanced filtering with URL-based options, multiselect support, contact display integration, interactive date pickers with timezone support, and toggleable stage functionality
  • Modular Architecture: GenericIndex refactored into standalone GenericGrid and GenericReportForm components for better reusability
  • Proposal System Integration: GenericQuote enhanced with comprehensive proposal generation capabilities
  • Smart Data Extraction: DropZone component with intelligent data extraction for complex API responses
  • Interactive Stage Management: Clickable stage toggles with API integration and visual feedback
  • Timezone Support: Automatic timezone detection and conversion for date operations
  • Contact Selection Workflows: Two-step date and contact selection for survey systems

Features

Authentication System

  • Login - Email/password authentication
  • Two-Factor Authentication (2FA) - Additional security layer with QR code and manual setup
  • Password Reset - Secure password recovery flow
  • Profile Management - User profile editing
  • SSO Integration - Support for Microsoft Azure
  • Remember Me - Persistent login option that works with 2FA

Data Visualization

  • DataGrid - Powerful data table with sorting, filtering, and pagination
  • Charts - Various chart types for data visualization
  • Report Builder - Custom report creation with table joins, column selection, and filtering
  • Sketch Field - Interactive drawing tools for diagrams and annotations

Form Components

  • Form Builder - Dynamic form creation and validation
  • Field Components - Text inputs, selects, checkboxes, etc.
  • Multi-select - Enhanced select with multiple selection support and selection limits
  • Autocomplete - Mapbox integration for location selection
  • Dropzone - Advanced file upload with drag and drop, smart data extraction, and file type icons
  • AsyncSelect - Asynchronous data loading for select components
  • Creatable Select - Select components that allow creating new options
  • GenericEditableTable - Interactive table component with inline editing, color-coded columns, and multiple data types

Navigation

  • Header/Navigation - Application header with navigation
  • Sub Navigation - Secondary navigation options
  • Breadcrumb - Path-based navigation component

Utility Components

  • Fetch - Advanced data fetching with intelligent caching, error handling, and TanStack Query integration
  • Download - File download functionality
  • Loader - Loading indicators
  • Notification - Toast notifications
  • StandardModal - Reusable modal component with consistent styling
  • Call Popup - Modal dialogs
  • QR Code - QR code generation and printing
  • CameraPlacement - Interactive camera placement tool with Verkada database, FOV visualization, and save/load functionality

Generic Pages

  • Index Pages - List view templates
  • Detail Pages - Item detail templates
  • Notification Pages - User notification center
  • Sorting Pages - Content ordering interfaces
  • Report Pages - Custom report building and execution
  • Audit Logs - System activity tracking and history

Installation

npm install visns-components
# or
yarn add visns-components

Usage

Basic Setup

import React from 'react';
import { GenericAuth } from 'visns-components';

const App = () => {
    return (
        <GenericAuth
            logo="/path/to/logo.png"
            headerLogo="/path/to/header-logo.png"
            providers={['azure']} // Optional SSO providers
            themeType="primary"
            navigation={navigationConfig}
            routeConfig={routeConfig}
        />
    );
};

export default App;

New Modular Architecture (v5.10.11+)

GenericIndex Component Refactoring

Starting from version 5.10.11, the GenericIndex component has been refactored for better maintainability and reusability:

Before: Single monolithic component (2,281 lines) After: Modular architecture with separated concerns

New Components

The refactoring introduced two new standalone components that can be used independently:

GenericGrid - Standalone grid component with full data management capabilities:

import { GenericGrid } from 'visns-components';

<GenericGrid 
  config={{
    settings: { 
      fetch: { 
        url: '/api/data',
        method: 'POST',
        params: { filter: 'active' }
      }
    },
    columns: [
      { name: 'id', header: 'ID', type: 'number' },
      { name: 'name', header: 'Name', type: 'text' },
      { name: 'date', header: 'Date', type: 'date' }
    ]
  }}
  userProfile={userProfile}
  onDataChange={(data) => console.log('Data updated:', data)}
  onRowClick={(row) => console.log('Row clicked:', row)}
/>

GenericReportForm - Standalone export form component:

import { GenericReportForm } from 'visns-components';

<GenericReportForm 
  config={{
    form: {
      title: 'Export Data',
      description: 'Select your export criteria below',
      filters: [
        { id: 'startDate', type: 'date', label: 'Start Date', required: true },
        { id: 'endDate', type: 'date', label: 'End Date', required: true },
        { id: 'format', type: 'dropdown', label: 'Format', options: [
          { value: 'csv', label: 'CSV' },
          { value: 'xlsx', label: 'Excel' }
        ]}
      ],
      url: '/api/export',
      method: 'POST',
      filename: 'export.csv'
    }
  }}
  userProfile={userProfile}
  onExport={(formData) => customExportHandler(formData)}
  onFormChange={(formData) => console.log('Form changed:', formData)}
/>

Benefits of the Refactoring

  • Reduced complexity: GenericIndex reduced from 2,281 to 1,031 lines (55% reduction)
  • Enhanced reusability: Grid and form components can be used independently
  • Better maintainability: Focused components are easier to debug and modify
  • Improved testing: Components can be unit tested separately
  • Backward compatibility: All existing GenericIndex usage remains unchanged

Shared Utilities

The refactoring also introduced shared utility functions:

  • shared/formatters.js: Cell content formatting and styling utilities
  • shared/gridUtils.js: Data operations (sorting, filtering, transformations)

These utilities are available for use in custom components and provide consistent functionality across the library.

GenericGrid Advanced Features

Filter Enhancements (v5.10.12+)

The GenericGrid component includes powerful filtering capabilities with URL-based options and multiselect support:

// URL-based filter options
{
  id: 'status',
  type: 'dropdown',
  label: 'Status',
  optionsUrl: '/api/filter-options/status',
  optionsMethod: 'POST',
  isMulti: true // Enables multiple selection
}

// Year filter with "now" default value
{
  id: 'year',
  type: 'year',
  label: 'Year',
  defaultValue: 'now' // Displays current year
}

Key Features:

  • Dynamic Options: Filters can fetch options from API endpoints using POST method
  • Multiselect Control: Use isMulti property for single/multiple selection control
  • Smart Defaults: Year filters properly handle "now" value to display current year
  • Value Initialization: Enhanced value formatting for MultiSelect components

Contact Display Integration (v5.11.1+)

GenericGrid automatically displays associated contacts when date fields have contact relationships:

// Date column with contact display
{
  id: 'survey_date',
  type: 'date',
  header: 'Survey Date',
  onClick: {
    type: 'date_with_contacts',
    title: 'Mark Survey Complete',
    message: 'Select the survey completion date'
  }
}

Features:

  • Date + Contact Display: Shows formatted date with contact summary badge
  • Contact Tooltips: Hover over contact badge for full contact details
  • Flexible Data Structure: Supports both existing contacts and manual entries
  • Responsive Design: Contact badges adapt to different screen sizes

Interactive Date Picker (v5.10.13+)

Enhanced date cell interaction with confirmation dialogs and timezone support:

// Date picker with timezone support
{
  id: 'completion_date',
  type: 'date',
  onClick: {
    type: 'date_picker',
    title: 'Set Completion Date',
    message: 'Choose the completion date',
    placeholder: 'Select date',
    timezone: 'auto', // or 'Australia/Perth'
    dateOnly: true // For DATE fields vs DATETIME
  }
}

Timezone Features (v5.10.15+):

  • Automatic Detection: timezone: "auto" detects browser timezone
  • Australia/Perth Support: Built-in support with forcePerth: true
  • Custom Timezones: Support any timezone with timezone: "specific/timezone"
  • UTC Conversion: Proper conversion between local timezone and UTC for API calls
  • Field Type Awareness: dateOnly parameter handles DATE vs DATETIME fields correctly

Contact Selection Workflow (v5.11.0+)

Two-step date and contact selection process for comprehensive data collection:

// Date + contact selection configuration
{
  id: 'survey_date',
  type: 'date',
  onClick: {
    type: 'date_with_contacts',
    contactConfig: {
      maxContacts: 5,
      allowManual: true,
      contactsEndpoint: '/api/contacts/search',
      requiredFields: ['name', 'email']
    }
  }
}

Contact Selection Features:

  • ContactSelectorModal: Modal for selecting existing or adding manual contacts
  • Multiple Contact Types: Database contacts (type: 'contact') and manual entries (type: 'manual')
  • Flexible Limits: Configurable contact limits and required fields
  • Survey Integration: Perfect for survey systems requiring contact associations

Toggleable Stage Functionality (v5.12.2+)

Interactive stage management with toggle functionality for both stage and stageCounter columns:

// Basic stage column with toggle
{
  id: 'opportunity_stage',
  type: 'stage',
  keyIds: ['prospect', 'qualified', 'proposal', 'won'],
  valueIds: ['Prospect', 'Qualified', 'Proposal', 'Won'],
  toggleable: true,
  toggleConfig: {
    updateUrl: '/api/opportunities/{id}/stage',
    updateMethod: 'PATCH',
    updateKey: 'stage_field'
  }
}

// StageCounter with individual stage toggles
{
  id: 'project_stages',
  type: 'stageCounter',
  stageConfig: {
    key: 'milestones',
    stageLabelKey: 'milestone_name',
    stageColour: {
      key: 'status',
      options: [
        { id: 'completed', colour: '#28a745' },
        { id: 'pending', colour: '#6c757d' }
      ]
    },
    toggleable: true,
    toggleConfig: {
      activeStatusValue: 'completed',
      inactiveStatusValue: 'pending',
      statusField: 'status',
      updateUrl: '/api/project-stages/{stage_id}',
      updateMethod: 'PATCH',
      updateKey: 'status'
    }
  }
}

Toggle Features:

  • Interactive Management: Click stages to enable/disable with visual feedback
  • API Integration: Configurable endpoints with URL placeholders and CSRF protection
  • Toast Notifications: Real-time success/error feedback
  • Auto-Refresh: Grid data refreshes after successful stage updates
  • Mixed Interactions: StageCounter supports both stage toggling and container navigation

DropZone Component Enhancements (v5.14.0+)

Smart Data Extraction

The DropZone component has been significantly enhanced with intelligent data extraction capabilities for complex API responses:

Problem Solved: When uploading files to nested relationships (e.g., project → estimation → design → lead → documents), the API returns different model structures at different stages:

  • Initial load: project.estimation.design.lead.documents
  • After upload: lead.documents (direct lead model response)

Solution: The DropZone component now automatically adapts the data extraction key based on the API response structure:

// Configuration
{
    "key": "estimation.design.lead.documents",
    "url": "/ajax/leads/{key}",
    "urlKey": "estimation.design.lead.id",
    "file_relationship": "documents"
}

// Smart adaptation:
// Initial: extracts from project.estimation.design.lead.documents
// After upload: automatically adapts to extract from response.data.documents

File Type Icons

Enhanced visual file identification with dynamic icons based on file extensions:

  • 📄 Documents: PDF, DOC, DOCX, TXT, RTF, ODT
  • 📊 Spreadsheets: XLS, XLSX, CSV, ODS
  • 🖼️ Images: JPG, JPEG, PNG, GIF, BMP, SVG, WEBP, ICO
  • 💻 Code Files: JS, JSX, TS, TSX, HTML, CSS, PHP, PY, JAVA, etc.
  • 📦 Archives: ZIP, RAR, 7Z, TAR, GZ, BZ2
  • 🎵 Audio: MP3, WAV, FLAC, AAC, OGG, M4A
  • 🎬 Video: MP4, AVI, MKV, MOV, WMV, FLV, WEBM

GenericDetail Integration

The GenericDetail component now uses the enhanced VisnsDropZone component instead of inline dropzone implementation, providing:

  • Consistent file handling across all dropzone instances
  • Automatic data refresh after file uploads
  • Smart data extraction for nested relationships
  • Improved state management and UI responsiveness

Technical Implementation

Key Features:

  • getAdaptedDataKey(): Intelligently adapts extraction paths based on response structure
  • getFileIcon(): Returns appropriate Lucide React icons based on file extensions
  • Enhanced fetchData integration: Automatic page refresh after successful uploads
  • Comprehensive debug logging for troubleshooting data extraction issues

Backward Compatibility: All existing DropZone implementations continue to work without modification. The smart features activate automatically when dataKey prop is provided.

Component Documentation

Authentication Components

GenericAuth

The main component that handles authentication and routing.

<GenericAuth
    layout="full" // 'full' or 'compact'
    loginBg="/path/to/background.jpg" // Optional login background
    logo="/path/to/logo.png" // Logo for login/auth screens
    headerLogo="/path/to/header-logo.png" // Logo for header in main app
    providers={['azure']} // Optional SSO providers
    navigation={navigationConfig} // Navigation configuration
    routeConfig={routeConfig} // Route configuration
    themeType="primary" // Theme type: 'primary', 'secondary', etc.
    enforce2FA={false} // Whether to enforce 2FA (default: false)
    clientPortalConfig={{
        component: <ClientPortalComponent />, // Main component for client portal
        urls: {
            login: '/clientPortal/login',
            verify: '/clientPortal/verify',
            logout: '/clientPortal/logout',
            profile: '/clientPortal/profile',
        },
        keys: {
            name: ['firstname', 'surname'], // Fields to use for client name display
        },
    }}
/>

Login

Handles user login with email and password.

<Login
    logo="/path/to/logo.png"
    providers={['azure']} // Optional SSO providers
    setSystemAuth={setAuthFunction}
    setUserProfile={setUserProfileFunction}
/>

TwoFactorAuth

Handles 2FA verification.

<TwoFactorAuth
    logo="/path/to/logo.png"
    setSystemAuth={setAuthFunction}
    setUserProfile={setUserProfileFunction}
/>

Reset

Password reset request form.

<Reset logo="/path/to/logo.png" setSystemAuth={setAuthFunction} />

Verify

Password reset verification form.

<Verify logo="/path/to/logo.png" setSystemAuth={setAuthFunction} />

Profile

User profile management.

<Profile
    userProfile={userProfileData}
    setUserProfile={setUserProfileFunction}
/>

DataGrid Component

A powerful data table component with sorting, filtering, and pagination. The DataGrid features a modular column renderer architecture with support for 28 different column types.

<DataGrid
    ajaxSetting={{
        url: '/api/data',
        method: 'GET',
        params: {},
    }}
    columns={[
        { name: 'id', headerName: 'ID', width: 90 },
        { name: 'name', headerName: 'Name', width: 150 },
        {
            name: 'department',
            headerName: 'Department',
            width: 180,
            groupSummaryReducer: 'count',
            groupSummaryRenderer: (count) => `${count} employees`,
        },
        {
            name: 'salary',
            headerName: 'Salary',
            width: 120,
            type: 'number',
            groupSummaryReducer: 'sum',
            groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
        },
        // More columns...
    ]}
    form={formConfig}
    gridHeight={500}
    defaultGroupBy={[{ name: 'department' }]}
    settings={{
        pagination: true,
        pageSize: 25,
        // More settings...
    }}
    setFilterData={setFilterFunction}
    setConfig={setConfigFunction}
    style={
        {
            /* Custom styles */
        }
    }
/>

Column Renderer Architecture

The DataGrid component utilizes a modular column renderer system with 28 specialized column types, all consolidated in the ColumnRenderers.jsx file. This architecture provides:

  • Consistent API: All column renderers follow a standardized interface
  • Extensibility: Easy to add new column types or modify existing ones
  • Maintainability: Centralized location for all column rendering logic
  • Performance: Optimized rendering for each specific column type
  • Intelligent Sorting: Automatic detection of sortable relationship and JSON fields

Supported Column Types:

  • boolean - Yes/No display with tooltips
  • currency - Formatted monetary values
  • date - Date formatting with validation
  • datetime - Date and time formatting
  • html5_date - HTML5 native date picker input
  • arrayCount - Count of array elements
  • colour - Color picker display
  • dropdown - Interactive dropdown selections with optional background color support
  • image - Image display from file URLs
  • file - File download links with icons
  • icons - Multiple icon display
  • json - JSON data extraction and display
  • number - Numeric values
  • option - Option mapping with placeholders
  • phone - Phone number formatting for Australian numbers
  • abn - Australian Business Number formatting
  • placeholder - Static placeholder text
  • age - Age calculation from dates
  • coding - Custom coding logic
  • relation - Related data display
  • relationArray - Array of related data
  • richtext - HTML content rendering
  • stage - Stage-based displays with optional toggle functionality
  • time - Time formatting
  • timer - Interactive timer controls
  • url - Clickable links
  • input_text - Inline text editing
  • address - Formatted address display
  • createdBy - User creation tracking
  • daterange - Date range formatting
  • stageCounter - Interactive visual stage progression with toggle functionality

All column renderers are exported from @visns-studio/visns-components and can be used individually or as part of the DataGrid component.

Dropdown Column Color Support

The dropdown column type now supports automatic background color application based on the selected option's colour property. This feature works in both DataGrid and GenericEditableTable components.

Configuration Example:

{
    id: 'status',
    label: 'Status',
    type: 'dropdown',
    options: [
        { id: 'Pending', label: 'Pending', colour: '#FFE5B4' },
        { id: 'Approved', label: 'Approved', colour: '#B4E5D1' },
        { id: 'Rejected', label: 'Rejected', colour: '#FFCDD2' },
        { id: 'Disputed', label: 'Disputed', colour: '#E1F5FE' }
    ],
    width: '10%'
}

Features:

  • Automatic Color Application: Cells automatically display the background color of the selected option
  • Total Column Integration: Total columns inherit the same background color as the status column in the same row
  • Consistent Styling: Black text color is automatically applied for optimal contrast
  • Flexible Configuration: Works with both static column options and dynamic dropdown data
  • Cross-Component Support: Available in both DataGrid and GenericEditableTable components

Example Usage in GenericEditableTable:

<GenericEditableTable
    schedulingConfig={{
        columns: [
            {
                id: 'status',
                label: 'Status',
                type: 'dropdown',
                options: [
                    { id: 'Pending', label: 'Pending', colour: '#FFE5B4' },
                    { id: 'Approved', label: 'Approved', colour: '#B4E5D1' }
                ]
            },
            {
                id: 'total',
                label: 'Total',
                type: 'total',
                keys: ['amount', 'tax'] // Will inherit status color
            }
        ]
    }}
/>

HTML5 Date Type

The html5_date column type provides native HTML5 date picker functionality in GenericEditableTable components.

Features:

  • Native Date Picker: Uses browser's built-in date picker for consistent UX
  • Cross-browser Support: Works across all modern browsers
  • Consistent Styling: Matches existing date field styling
  • Read-only Support: Respects column read-only configuration
  • Change Integration: Fully integrated with existing field change handlers

Configuration Example:

{
    id: 'start_date',
    label: 'Start Date',
    type: 'html5_date',
    width: '15%'
}

Phone and ABN Formatting

The DataGrid and GenericDetail components include specialized formatting for Australian phone numbers and ABNs (Australian Business Numbers).

Phone Number Formatting

The phone column type automatically formats phone numbers according to Australian standards:

Supported Formats:

  • Mobile Numbers: 0400 000 000 (04XX XXX XXX)
  • 1300/1800 Numbers: 1300 000 000 (1300 XXX XXX)
  • Landline Numbers: (02) 0000 0000 ((0X) XXXX XXXX)
  • Generic Formatting: Applied to other number patterns
  • Fallback: Returns original value if no pattern matches

Configuration Examples:

DataGrid:

{
    id: 'phone',
    label: 'Phone Number',
    type: 'phone',
    width: '15%'
}

GenericDetail:

{
    id: 'phone',
    label: 'Phone Number',
    type: 'phone',
    relation: ['company'] // Also works with relationship fields
}

JSON Fields with Format:

{
    id: 'contact_data',
    label: 'Contact',
    type: 'json',
    jsonData: 'phone',
    format: 'phone' // Applies phone formatting to JSON field values
}
ABN Formatting

The abn column type formats Australian Business Numbers:

Format: 00 000 000 000 (XX XXX XXX XXX)

  • Validates 11-digit ABNs
  • Returns original value if not valid format

Configuration Example:

{
    id: 'abn',
    label: 'ABN',
    type: 'abn',
    width: '15%'
}

Intelligent Relationship Sorting

The DataGrid component includes intelligent sorting capabilities that automatically detect and enable sorting for relationship and JSON fields. This feature works seamlessly with the backend HasRelationshipSorting trait.

Automatic Sortable Detection:

// The DataGrid automatically detects sortable fields based on naming patterns
<DataGrid
    columns={[
        {
            name: 'user.profile.name',     // Relationship field - automatically sortable
            headerName: 'User Name',
            nameFrom: ['user', 'profile', 'name']
        },
        {
            name: 'settings.theme',        // JSON field - automatically sortable  
            headerName: 'Theme Preference',
            nameFrom: ['settings', 'theme']
        },
        {
            name: 'client.company',        // BelongsTo relationship - automatically sortable
            headerName: 'Company',
            nameFrom: ['client', 'company']
        }
    ]}
    settings={{
        intelligentSorting: true  // Enable intelligent sorting (default: true)
    }}
/>

Manual Sortable Control:

// Override automatic detection with explicit sortable configuration
<DataGrid
    columns={[
        {
            name: 'user.profile.name',
            headerName: 'User Name',
            sortable: true,  // Explicitly enable sorting
            nameFrom: ['user', 'profile', 'name']
        },
        {
            name: 'complex.calculation',
            headerName: 'Complex Field',
            sortable: false, // Explicitly disable sorting
            nameFrom: ['complex', 'calculation']
        }
    ]}
/>

Supported Sorting Patterns:

The intelligent sorting system recognizes these field patterns as sortable:

  • Relationship Fields: user.profile.name, order.customer.company
  • JSON Fields: settings.theme, metadata.tags, data.preferences.language
  • Standard Fields: Any direct model attribute
  • Nested Relationships: post.author.profile.display_name

Field Name Detection:

The sorting system intelligently determines sortable fields using:

  1. nameFrom Array: Primary method for complex field paths

    nameFrom: ['user', 'profile', 'full_name']  // → user.profile.full_name
  2. Dot Notation: Direct string paths

    name: 'client.address.city'  // → client.address.city
  3. Pattern Recognition: Automatic detection of common relationship patterns

    • Fields ending with _id that have corresponding relationships
    • JSON field patterns like data.*, settings.*, metadata.*
    • Nested object notation

Configuration Options:

<DataGrid
    settings={{
        // Global intelligent sorting toggle
        intelligentSorting: true,  // default: true
        
        // Additional sorting configuration
        sortingConfig: {
            // Custom sortable field patterns
            additionalPatterns: [
                /^custom_data\./,   // Match custom_data.* fields
                /^config\./         // Match config.* fields
            ],
            
            // Fields to exclude from automatic sorting
            excludePatterns: [
                /^temp\./,          // Exclude temp.* fields
                /^cache\./          // Exclude cache.* fields
            ]
        }
    }}
/>

Backend Integration:

The frontend intelligent sorting works seamlessly with the backend HasRelationshipSorting trait:

// Frontend automatically generates these API calls:
// GET /ajax/users/table?orderBy=profile.name&order=asc
// GET /ajax/orders/table?orderBy=customer.company&order=desc
// GET /ajax/products/table?orderBy=metadata.category&order=asc

Benefits:

  • Zero Configuration: Most relationship and JSON fields work automatically
  • Performance Optimized: Uses backend subqueries instead of joins
  • Type Safety: Automatic validation of sortable field patterns
  • Fallback Support: Graceful handling of unsupported sort fields
  • Developer Friendly: Clear visual indicators for sortable columns

DataGrid Props

| Prop | Description | Type | Default | | --------------------- | ------------------------------------------ | -------- | ------- | | ajaxSetting | Configuration for data fetching | Object | - | | columns | Column definitions | Array | - | | defaultExpandedNodes | Initial expanded nodes for TreeGrid | Object | - | | defaultGroupBy | Initial grouping configuration | Array | - | | expandedNodes | Current expanded nodes for TreeGrid | Object | - | | form | Form configuration for editing | Object | - | | gridHeight | Height of the grid | Number | 500 | | groupBy | Current grouping configuration | Array | - | | loadNode | Function to load TreeGrid nodes on demand | Function | - | | onExpandedNodesChange | Callback for TreeGrid node expand/collapse | Function | - | | onGroupByChange | Callback for grouping changes | Function | - | | renderGroupTitle | Custom renderer for group headers | Function | - | | settings | Grid settings (pagination, etc.) | Object | - | | setFilterData | Callback for filter changes | Function | - | | setConfig | Callback for configuration changes | Function | - | | style | Custom styles | Object | - | | treeColumn | Column name to display TreeGrid hierarchy | String | - | | treeNestingSize | Indentation size for TreeGrid levels | Number | 20 |

Auto-Refresh Functionality

The DataGrid component includes an auto-refresh feature that automatically reloads data at specified intervals. This is useful for displaying real-time or frequently updated data.

To enable auto-refresh:

<DataGrid
    ajaxSetting={{
        url: '/api/data',
        method: 'GET',
        params: {},
        autoRefresh: 30, // Refresh every 30 seconds
        // or
        autoRefresh: true, // Use default 30-second interval
    }}
    // Other props...
/>

When auto-refresh is configured:

  • A checkbox appears in the DataGrid header to enable/disable auto-refresh
  • Auto-refresh is enabled by default when configured
  • The interval can be customized (in seconds) by setting a numeric value
  • Setting autoRefresh: true uses the default 30-second interval
  • The component silently refreshes data without showing notifications

Group By Functionality

The DataGrid component supports grouping data by one or more columns, allowing you to organize and visualize data in hierarchical structures.

Using defaultGroupBy

The defaultGroupBy property allows you to specify initial grouping when the DataGrid loads:

<DataGrid
    // Other props...
    defaultGroupBy={[
        { name: 'category' }, // Group by a single column
    ]}
/>

For more complex grouping with multiple levels:

<DataGrid
    // Other props...
    defaultGroupBy={[
        { name: 'department' }, // First level grouping
        { name: 'role' }, // Second level grouping
    ]}
/>
Dynamic Grouping

You can also control grouping programmatically:

const [groupBy, setGroupBy] = useState([{ name: 'category' }]);

// Later in your component
<DataGrid
    // Other props...
    groupBy={groupBy}
    onGroupByChange={setGroupBy}
/>;
Server-Side Grouping

For server-side grouping, the DataGrid sends the groupBy information to your API endpoint:

<DataGrid
    ajaxSetting={{
        url: '/api/data',
        method: 'POST',
        // Other settings...
    }}
    defaultGroupBy={[{ name: 'category' }]}
    // Other props...
/>

Your server will receive a request with groupBy information:

{
    "page": 1,
    "take": 25,
    "groupBy": [{ "name": "category" }]
    // Other parameters...
}
Group Summaries

You can add summary calculations to grouped data:

<DataGrid
    // Other props...
    columns={[
        {
            name: 'amount',
            headerName: 'Amount',
            type: 'number',
            groupSummaryReducer: 'sum', // Options: 'sum', 'avg', 'min', 'max', 'count'
            groupSummaryRenderer: (summary) => `Total: $${summary.toFixed(2)}`,
        },
        // Other columns...
    ]}
    defaultGroupBy={[{ name: 'category' }]}
/>

Group-Level Bulk Edit Operations (Enhanced v5.13.0+)

The DataGrid component now supports powerful bulk editing capabilities through group-based operations, allowing users to efficiently manage multiple items simultaneously.

New in v5.13.0:

  • Dropdown Column Color Support: Automatic background color application for dropdown columns based on option colors
  • Total Column Color Integration: Total columns now inherit background colors from associated status columns
  • HTML5 Date Type: Added native HTML5 date picker support for GenericEditableTable components
  • Enhanced GenericEditableTable: Improved column type support and consistent styling across components

How Group Bulk Edit Works

When data is grouped using the groupBy or defaultGroupBy properties, each group header can display action icons for common operations. The new bulk edit functionality adds an "edit" icon that opens a form modal for updating all items in the group simultaneously.

Configuration Structure

Group operations are configured through the ajaxSetting.groupBySetting.icons array:

<DataGrid
    ajaxSetting={{
        url: '/api/data',
        groupBy: ['category'],
        groupBySetting: {
            icons: [
                {
                    id: 'edit',
                    label: 'Edit Group',
                    formModal: {
                        title: 'Bulk Edit Items',
                        url: '/ajax/data/bulkFormUpdate',
                        method: 'POST',
                        groupKey: 'category',
                        fields: ['assigned_user_id', 'status'],
                        data: {
                            stage: 'processing'
                        }
                    },
                    show: [
                        {
                            id: 'status',
                            value: 'pending'
                        }
                    ]
                }
            ]
        }
    }}
    // Other props...
/>

Configuration Properties

Icon Configuration:

  • id: Must be set to 'edit' to enable bulk edit functionality
  • label: Display text for the action button
  • formModal: Configuration object for the bulk edit form
  • show: Optional conditions to control when the icon appears

Form Modal Configuration:

  • title: Title displayed in the bulk edit modal
  • url: API endpoint for bulk update requests
  • method: HTTP method (typically 'POST')
  • groupKey: Field name used for grouping (matches the groupBy value)
  • fields: Array of field IDs that can be bulk edited
  • data: Additional data to send with the bulk update request

Form Integration

The bulk edit feature integrates seamlessly with the existing Form component. When the edit icon is clicked:

  1. A form modal opens with the specified title
  2. Only the fields listed in the fields array are displayed
  3. The form shows an indication that this is a bulk operation
  4. On submission, all items in the group are updated simultaneously

Backend Integration

The bulk edit functionality expects a specific API endpoint structure:

// POST /ajax/data/bulkFormUpdate
{
    "bulkEdit": true,
    "groupValue": "Category Name",
    "groupKey": "category", 
    "rowIds": [1, 2, 3, 4],
    "assigned_user_id": 5,
    "status": "in_progress",
    // Additional form fields...
}

// Expected Response:
{
    "error": "",
    "message": "Bulk update completed successfully",
    "data": {
        "updated_count": 4,
        "group_value": "Category Name",
        "updated_items": [...]
    }
}

Features and Benefits

User Experience:

  • No Confirmation Dialog: Edit actions open the form immediately (unlike timer operations)
  • No Toast Notifications: Silent operation that focuses on the form interaction
  • Visual Group Context: Modal title includes group information and item count
  • Field Filtering: Only relevant fields are shown for bulk editing

Technical Features:

  • Transaction Safety: All updates wrapped in database transactions
  • Partial Updates: Only changed fields are updated
  • Audit Trail: Full change tracking for compliance
  • Error Handling: Graceful handling of validation errors and conflicts
  • Performance Optimized: Efficient bulk operations with minimal database calls

Advanced Example

// Manufacturing workflow with bulk edit capabilities
<DataGrid
    ajaxSetting={{
        url: '/ajax/manufacturing/items',
        groupBy: ['work_order'],
        groupBySetting: {
            icons: [
                // Bulk edit for user assignment
                {
                    id: 'edit',
                    label: 'Assign Operator',
                    formModal: {
                        title: 'Bulk Assign Operator',
                        url: '/ajax/manufacturing/bulkAssign',
                        method: 'POST',
                        groupKey: 'work_order',
                        fields: ['operator_id', 'priority'],
                        data: {
                            stage: 'production',
                            update_type: 'assignment'
                        }
                    },
                    show: [
                        { id: 'status', value: 'ready' }
                    ]
                },
                // Timer operations (existing functionality)
                {
                    id: 'clock',
                    label: 'Start Production',
                    fetch: {
                        url: '/ajax/manufacturing/groupTimer',
                        method: 'POST',
                        data: {
                            action: 'start_production',
                            timestamp: 'now()'
                        }
                    }
                }
            ]
        }
    }}
    columns={[
        { name: 'item_code', headerName: 'Item Code' },
        { name: 'operator', headerName: 'Operator' },
        { name: 'status', headerName: 'Status' },
        { name: 'priority', headerName: 'Priority' }
    ]}
    form={{
        fields: [
            {
                id: 'operator_id',
                label: 'Production Operator',
                type: 'dropdown-ajax',
                url: '/ajax/users/operators',
                required: true
            },
            {
                id: 'priority',
                label: 'Priority Level',
                type: 'select',
                options: [
                    { value: 'normal', label: 'Normal' },
                    { value: 'high', label: 'High' },
                    { value: 'urgent', label: 'Urgent' }
                ]
            }
        ]
    }}
/>

This implementation provides a powerful and intuitive way to manage bulk operations in grouped data scenarios, significantly improving workflow efficiency for users managing large datasets.

Customizing Group Rendering

You can customize how groups are displayed:

<DataGrid
    // Other props...
    renderGroupTitle={(groupData) => (
        <div style={{ fontWeight: 'bold', color: 'var(--primary-color)' }}>
            {groupData.name}: {groupData.value} ({groupData.count} items)
        </div>
    )}
    defaultGroupBy={[{ name: 'category' }]}
/>
Complete Example with Dynamic Grouping

Here's a complete example of a component that uses DataGrid with dynamic grouping controls:

import React, { useState, useEffect } from 'react';
import { DataGrid } from 'visns-components';

const EmployeeDataGrid = () => {
    // State for grouping
    const [groupBy, setGroupBy] = useState([]);

    // Sample data for the grid
    const [employees, setEmployees] = useState([]);

    // Fetch data on component mount
    useEffect(() => {
        const fetchEmployees = async () => {
            try {
                const response = await fetch('/api/employees');
                const data = await response.json();
                setEmployees(data);
            } catch (error) {
                console.error('Error fetching employees:', error);
            }
        };

        fetchEmployees();
    }, []);

    // Column definitions
    const columns = [
        {
            name: 'id',
            headerName: 'ID',
            width: 80,
        },
        {
            name: 'name',
            headerName: 'Name',
            width: 180,
        },
        {
            name: 'department',
            headerName: 'Department',
            width: 150,
            groupSummaryReducer: 'count',
            groupSummaryRenderer: (count) => `${count} employees`,
        },
        {
            name: 'role',
            headerName: 'Role',
            width: 150,
        },
        {
            name: 'salary',
            headerName: 'Salary',
            width: 120,
            type: 'number',
            groupSummaryReducer: 'sum',
            groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
        },
        {
            name: 'hireDate',
            headerName: 'Hire Date',
            width: 120,
            type: 'date',
        },
    ];

    // Group by options for the dropdown
    const groupByOptions = [
        { label: 'No Grouping', value: 'none' },
        { label: 'Department', value: 'department' },
        { label: 'Role', value: 'role' },
        { label: 'Department & Role', value: 'department-role' },
    ];

    // Handle group by change
    const handleGroupByChange = (e) => {
        const value = e.target.value;

        switch (value) {
            case 'department':
                setGroupBy([{ name: 'department' }]);
                break;
            case 'role':
                setGroupBy([{ name: 'role' }]);
                break;
            case 'department-role':
                setGroupBy([{ name: 'department' }, { name: 'role' }]);
                break;
            default:
                setGroupBy([]);
                break;
        }
    };

    return (
        <div>
            <div style={{ marginBottom: '1rem' }}>
                <label htmlFor="groupBy">Group By: </label>
                <select
                    id="groupBy"
                    onChange={handleGroupByChange}
                    style={{ padding: '0.5rem', borderRadius: '4px' }}
                >
                    {groupByOptions.map((option) => (
                        <option key={option.value} value={option.value}>
                            {option.label}
                        </option>
                    ))}
                </select>
            </div>

            <DataGrid
                columns={columns}
                dataSource={employees}
                groupBy={groupBy}
                onGroupByChange={setGroupBy}
                gridHeight={600}
                settings={{
                    pagination: true,
                    pageSize: 25,
                }}
                renderGroupTitle={(groupData) => (
                    <div
                        style={{
                            fontWeight: 'bold',
                            color: 'var(--primary-color)',
                            padding: '0.25rem 0',
                        }}
                    >
                        {groupData.name === 'department'
                            ? 'Department'
                            : 'Role'}
                        :<span style={{ marginLeft: '0.5rem' }}>
                            {groupData.value} ({groupData.count} employees)
                        </span>
                    </div>
                )}
            />
        </div>
    );
};

export default EmployeeDataGrid;

TreeGrid Functionality

The DataGrid component also supports hierarchical data display through its TreeGrid functionality. This allows you to represent parent-child relationships in your data with expandable/collapsible nodes.

Basic TreeGrid Configuration

To use the TreeGrid functionality:

<DataGrid
    // Other props...
    treeColumn="name" // Specify which column will display the expand/collapse icons
    dataSource={hierarchicalData} // Data with parent-child relationships
/>

Your data should have a structure where parent nodes contain child nodes:

const hierarchicalData = [
    {
        id: 1,
        name: 'Parent 1',
        nodes: [
            // Child nodes
            { id: 11, name: 'Child 1.1' },
            {
                id: 12,
                name: 'Child 1.2',
                nodes: [
                    // Nested children
                    { id: 121, name: 'Grandchild 1.2.1' },
                ],
            },
        ],
    },
    {
        id: 2,
        name: 'Parent 2',
        nodes: [{ id: 21, name: 'Child 2.1' }],
    },
];
Controlling Expanded Nodes

You can control which nodes are expanded:

const [expandedNodes, setExpandedNodes] = useState({
    1: true, // Node with ID 1 is expanded
    '1/12': true, // Node with path 1/12 is expanded
});

<DataGrid
    // Other props...
    treeColumn="name"
    dataSource={hierarchicalData}
    expandedNodes={expandedNodes}
    onExpandedNodesChange={setExpandedNodes}
/>;
Asynchronous Node Loading

For large datasets, you can load child nodes on demand:

<DataGrid
    // Other props...
    treeColumn="name"
    dataSource={asyncHierarchicalData}
    loadNode={({ node }) => {
        // Return a Promise that resolves to the child nodes
        return fetchChildNodes(node.id);
    }}
/>

For asynchronous loading, parent nodes should have nodes: null to indicate they have children that need to be loaded.

Complete TreeGrid Example

Here's a complete example of a component that uses DataGrid with TreeGrid functionality:

import React, { useState } from 'react';
import { DataGrid } from 'visns-components';

const OrganizationTreeGrid = () => {
    // Sample hierarchical data
    const departments = [
        {
            id: 1,
            name: 'Executive',
            budget: 1500000,
            nodes: [
                {
                    id: 11,
                    name: 'Finance',
                    budget: 850000,
                    nodes: [
                        { id: 111, name: 'Accounting', budget: 350000 },
                        { id: 112, name: 'Investment', budget: 500000 },
                    ],
                },
                {
                    id: 12,
                    name: 'Operations',
                    budget: 650000,
                    nodes: [
                        { id: 121, name: 'HR', budget: 200000 },
                        { id: 122, name: 'Facilities', budget: 450000 },
                    ],
                },
            ],
        },
        {
            id: 2,
            name: 'Sales & Marketing',
            budget: 1200000,
            nodes: [
                { id: 21, name: 'Sales', budget: 700000 },
                { id: 22, name: 'Marketing', budget: 500000 },
            ],
        },
        {
            id: 3,
            name: 'Technology',
            budget: 2000000,
            nodes: [
                { id: 31, name: 'Development', budget: 1200000 },
                { id: 32, name: 'IT Support', budget: 800000 },
            ],
        },
    ];

    // State for expanded nodes
    const [expandedNodes, setExpandedNodes] = useState({
        1: true, // Executive department is expanded by default
        3: true, // Technology department is expanded by default
    });

    // Column definitions
    const columns = [
        {
            name: 'name',
            headerName: 'Department',
            width: 250,
            // This column will display the tree expand/collapse icons
        },
        {
            name: 'budget',
            headerName: 'Budget',
            width: 150,
            type: 'number',
            render: ({ value }) => `$${value.toLocaleString()}`,
        },
    ];

    return (
        <div>
            <h2>Organization Structure</h2>

            <DataGrid
                columns={columns}
                dataSource={departments}
                treeColumn="name"
                expandedNodes={expandedNodes}
                onExpandedNodesChange={setExpandedNodes}
                treeNestingSize={30} // Indentation size for nested levels
                gridHeight={500}
                idProperty="id"
            />
        </div>
    );
};

export default OrganizationTreeGrid;

Form Components

Form

A flexible form component with validation.

<Form
    fields={[
        {
            name: 'firstName',
            label: 'First Name',
            type: 'text',
            required: true,
        },
        { name: 'email', label: 'Email', type: 'email', required: true },
        // More fields...
    ]}
    onSubmit={handleSubmit}
    initialValues={initialData}
    validationSchema={validationSchema}
/>

AsyncSelect

Asynchronous select component with search functionality and dynamic data loading.

<AsyncSelect
    url="/api/options"
    valueKey="id"
    labelKey="name"
    placeholder="Select an option"
    onChange={handleChange}
    inputValue={selectedValue}
    settings={{
        id: 'myAsyncSelect',
        params: { additionalParam: 'value' }, // Optional: additional parameters to send with the request
    }}
    isCreatable={false} // Optional: allow creating new options
    creatableConfig={{
        // Configuration for creatable options
        url: '/api/options/create',
        method: 'POST',
    }}
/>

The AsyncSelect component supports these features:

  • Dynamic Data Loading: Loads options from an API endpoint as the user types
  • Debounced Requests: Prevents excessive API calls during typing
  • Custom Parameters: Send additional parameters with the API request
  • Creatable Options: Allow users to create new options that don't exist in the API

MultiSelect

Select component with multiple selection support and optional selection limits.

<MultiSelect
    options={options}
    inputValue={selectedValues}
    onChange={handleChange}
    placeholder="Select options"
    multi={true}
    settings={{
        id: 'myMultiSelect',
        limit: 3, // Optional: limit the number of selections
    }}
    isCreatable={false} // Optional: allow creating new options
    creatableConfig={{}} // Configuration for creatable options
/>

The MultiSelect component supports these features:

  • Selection Limits: Set a maximum number of items that can be selected
  • Single Item Mode: When limit={1} with multi={true}, it keeps only the most recently selected item
  • Creatable Options: Allow users to create new options that don't exist in the list

Autocomplete

Location autocomplete with Mapbox integration.

<Autocomplete
    apiKey="your-mapbox-api-key"
    placeholder="Enter a location"
    onChange={handleLocationChange}
    value={location}
/>

DropZone

Advanced file upload component with drag and drop support, smart data extraction, and file type icons.

Key Features:

  • Smart Data Extraction: Automatically adapts to API response structures using nested keys
  • File Type Icons: Dynamic icons based on file extensions (PDF, images, documents, etc.)
  • Nested API Support: Handles complex data structures like estimation.design.lead.documents
  • Auto-Refresh: Intelligent file list updates after upload
  • Gallery Mode: Image preview and gallery display
  • Progress Tracking: Upload progress indication
  • File Management: Edit, delete, and download functionality

Basic Usage:

<DropZone
    fetchData={handleRefresh}
    files={currentFiles}
    settings={{
        url: '/ajax/files',
        method: 'POST',
        type: 'gallery', // or 'list'
        data: {
            file_relationship: 'documents'
        },
        folder: 'uploads',
        filetype: 'image',
        deleteUrl: '/ajax/files/delete'
    }}
/>

Smart Data Extraction:

<DropZone
    fetchData={handleRefresh}
    files={files}
    url="/ajax/leads/{key}"
    urlKey="estimation.design.lead.id"
    dataKey="documents" // Extracts files from response.data.documents
    entityData={projectData}
    routeParams={routeParams}
    settings={{
        method: 'PUT',
        data: {
            file_relationship: 'documents'
        }
    }}
/>

Supported File Types & Icons:

  • 📄 Documents: PDF, DOC, DOCX, TXT, RTF, ODT
  • 📊 Spreadsheets: XLS, XLSX, CSV, ODS
  • 🖼️ Images: JPG, JPEG, PNG, GIF, BMP, SVG, WEBP, ICO
  • 💻 Code Files: JS, JSX, TS, TSX, HTML, CSS, PHP, PY, JAVA, etc.
  • 📦 Archives: ZIP, RAR, 7Z, TAR, GZ, BZ2
  • 🎵 Audio: MP3, WAV, FLAC, AAC, OGG, M4A
  • 🎬 Video: MP4, AVI, MKV, MOV, WMV, FLV, WEBM

Configuration Options:

  • fetchData: Function called after successful uploads to refresh data
  • files: Array of current files to display
  • url: Upload endpoint (supports {key} placeholders)
  • urlKey: Dot notation path for URL parameter replacement
  • dataKey: Dot notation path for extracting files from API responses
  • deleteUrl: Endpoint for file deletion
  • entityData: Context data for URL parameter resolution
  • routeParams: Route parameters for URL processing
  • settings.type: 'gallery' for images, 'list' for documents
  • settings.filetype: File type restrictions
  • settings.folder: Upload folder designation

Association Management Components

AssociationManager

A sophisticated component for managing entity relationships and associations within your application. The AssociationManager provides an intuitive interface for creating, viewing, and managing complex many-to-many relationships between different data entities.

<AssociationManager
    config={{
        title: "Client Associations",
        description: "Manage relationships between clients and projects",
        primaryEntity: {
            id: "clients",
            label: "Clients",
            displayKey: "name",
            fetchUrl: "/api/clients"
        },
        secondaryEntity: {
            id: "projects", 
            label: "Projects",
            displayKey: "title",
            fetchUrl: "/api/projects"
        },
        association: {
            fetchUrl: "/api/client-project-associations",
            createUrl: "/api/client-project-associations",
            deleteUrl: "/api/client-project-associations",
            pivotTable: "client_project"
        },
        permissions: {
            canCreate: true,
            canDelete: true,
            canView: true
        },
        ui: {
            searchPlaceholder: "Search clients or projects...",
            emptyStateMessage: "No associations found",
            confirmDeleteMessage: "Are you sure you want to remove this association?"
        }
    }}
    userProfile={userProfile}
    onAssociationChange={(data) => console.log('Association changed:', data)}
/>

Key Features:

  1. Bidirectional Relationship Management:

    • Create associations from either entity perspective
    • Automatic relationship synchronization
    • Real-time updates across related views
  2. Advanced Search and Filtering:

    • Multi-entity search capabilities
    • Filter by entity type, status, or custom attributes
    • Debounced search to optimize performance
  3. Bulk Operations:

    • Select multiple entities for batch association
    • Bulk delete with confirmation dialogs
    • Mass import/export functionality
  4. Visual Association Mapping:

    • Interactive relationship visualization
    • Drag-and-drop association creation
    • Color-coded entity types
  5. Permission-Based Access Control:

    • Granular permissions for create, read, update, delete
    • Role-based access to specific entity types
    • Custom permission validation

Configuration Options:

const associationConfig = {
    // Required: Basic entity configuration
    primaryEntity: {
        id: "clients",                    // Unique identifier
        label: "Clients",                 // Display name
        displayKey: "name",               // Field to show in lists
        fetchUrl: "/api/clients",         // API endpoint
        searchFields: ["name", "email"],  // Fields to search
        icon: "Users",                    // Lucide icon name
        color: "#3b82f6"                  // Brand color
    },
    
    secondaryEntity: {
        id: "projects",
        label: "Projects", 
        displayKey: "title",
        fetchUrl: "/api/projects",
        searchFields: ["title", "description"],
        icon: "FolderOpen",
        color: "#10b981"
    },

    // Association configuration
    association: {
        fetchUrl: "/api/associations",     // Get existing associations
        createUrl: "/api/associations",    // Create new associations
        updateUrl: "/api/associations",    // Update associations (optional)
        deleteUrl: "/api/associations",    // Delete associations
        pivotTable: "client_project",      // Database table name
        
        // Additional pivot data
        pivotFields: [
            {
                name: "role",
                label: "Role",
                type: "select",
                options: [
                    { value: "manager", label: "Manager" },
                    { value: "contributor", label: "Contributor" }
                ]
            },
            {
                name: "start_date",
                label: "Start Date",
                type: "date"
            }
        ]
    },

    // UI customization
    ui: {
        layout: "split",                   // "split", "tabs", "cards"
        theme: "default",                  // "default", "compact", "detailed"
        showEntityCounts: true,            // Show association counts
        showLastModified: true,            // Show modification timestamps
        enableDragDrop: true,              // Enable drag-and-drop
        confirmActions: true,              // Show confirmation dialogs
        
        // Custom labels and messages
        labels: {
            createButton: "Add Association",
            deleteButton: "Remove",
            searchPlaceholder: "Search entities...",
            noResultsMessage: "No entities found",
            loadingMessage: "Loading associations..."
        }
    },

    // Validation rules
    validation: {
        preventDuplicates: true,           // Prevent duplicate associations
        requirePivotData: false,           // Require additional pivot fields
        customValidation: (primary, secondary, pivotData) => {
            // Custom validation logic
            return { valid: true, message: "" };
        }
    },

    // Performance optimization
    performance: {
        enableVirtualization: true,        // For large datasets
        pageSize: 50,                      // Pagination size
        debounceMs: 300,                   // Search debounce
        cacheResults: true                 // Cache API responses
    }
};

API Integration:

The AssociationManager expects specific API response formats:

// GET /api/clients (Primary Entity)
{
    "data": [
        {
            "id": 1,
            "name": "Acme Corp",
            "email": "[email protected]",
            "created_at": "2023-01-01T00:00:00Z"
        }
    ],
    "total": 150,
    "page": 1,
    "per_page": 50
}

// GET /api/associations (Existing Associations)
{
    "data": [
        {
            "id": 1,
            "primary_id": 1,
            "secondary_id": 5,
            "pivot_data": {
                "role": "manager",
                "start_date": "2023-01-01"
            },
            "primary": {
                "id": 1,
                "name": "Acme Corp"
            },
            "secondary": {
                "id": 5,
                "title": "Website Redesign"
            },
            "created_at": "2023-01-01T00:00:00Z"
        }
    ]
}

// POST /api/associations (Create Association)
{
    "primary_id": 1,
    "secondary_id": 5,
    "pivot_data": {
        "role": "manager",
        "start_date": "2023-01-01"
    }
}

// Response:
{
    "success": true,
    "data": {
        "id": 1,
        "primary_id": 1,
        "secondary_id": 5,
        "created_at": "2023-01-01T00:00:00Z"
    },
    "message": "Association created successfully"
}

Event Handling:

<AssociationManager
    config={associationConfig}
    onAssociationChange={(event) => {
        // event.type: 'created', 'updated', 'deleted'
        // event.data: Association data
        // event.entities: Affected entities
        console.log('Association event:', event);
    }}
    onEntitySelect={(entity, type) => {
        // Handle entity selection
        console.log('Entity selected:', entity, type);
    }}
    onError={(error) => {
        // Handle errors
        console.error('Association error:', error);
    }}
/>

Advanced Usage Examples:

  1. Client-Project Association with Roles:
// Managing client assignments to projects with specific roles
<AssociationManager
    config={{
        primaryEntity: { id: "clients", label: "Clients", ... },
        secondaryEntity: { id: "projects", label: "Projects", ... },
        association: {
            pivotFields: [
                {
                    name: "role",
                    label: "Role in Project",
                    type: "select",
                    required: true,
                    options: [
                        { value: "sponsor", label: "Project Sponsor" },
                        { value: "stakeholder", label: "Key Stakeholder" },
                        { value: "user", label: "End User" }
                    ]
                }
            ]
        }
    }}
/>
  1. User-Permission Association:
// Managing user permissions with expiration dates
<AssociationManager
    config={{
        primaryEntity: { id: "users", label: "Users", ... },
        secondaryEntity: { id: "permissions", label: "Permissions", ... },
        association: {
            pivotFields: [
                {
                    name: "expires_at",
                    label: "Expiration Date",
                    type: "datetime",
                    required: false
                },
                {
                    name: "granted_by",
                    label: "Granted By",
                    type: "select",
                    fetchUrl: "/api/users/managers"
                }
            ]
        }
    }}
/>

Business Card OCR Component

BusinessCardOcr (Enhanced v5.11.1+)

An advanced OCR (Optical Character Recognition) component specifically designed for scanning business cards and automatically extracting contact information. The component provides intelligent text recognition, client matching, and seamless contact management integration.

<BusinessCardOcr
    isOpen={showScanner}
    onClose={() => setShowScanner(false)}
    onContactSaved={(contact) => {
        console.log('Contact saved:', contact);
        // Handle the saved contact data
    }}
    fields={[
        { id: 'name', label: 'Full Name', required: tru