Introduction

The Questionnaire List Module is a comprehensive React-Redux based system that provides a complete management interface for questionnaires within the Nimbly audit admin application. This module implements sophisticated CRUD operations, bulk processing capabilities, and advanced filtering/sorting mechanisms for managing questionnaire templates and instances.

The system is built using modern React patterns with TypeScript support, Redux Saga for async operations, styled-components for UI styling, and Firebase for real-time data synchronization. The architecture follows a layered approach with clear separation of concerns between presentation, business logic, and data access layers.

Key Features:

  • Comprehensive Listing Interface: Paginated, sortable, and filterable questionnaire management
  • Bulk Operations: Excel-based template uploads and multi-select download functionality
  • Clone Management: Intelligent duplication of questionnaires with metadata preservation
  • Soft Delete System: Safe questionnaire removal with recovery capabilities
  • Real-time Synchronization: Firebase-powered live updates across multiple sessions
  • Advanced Filtering: Multi-criteria search and filter capabilities
  • Permission-based Access: Role-based operation restrictions and feature flags

Architecture Overview

The Questionnaire List Module follows a sophisticated multi-layered architecture that separates concerns across presentation, business logic, data access, and state management layers.

graph TB
    subgraph "Presentation Layer"
        A[QuestionnaireManager] --> B[QuestionnaireList]
        A --> C[QuestionnaireListHeader]
        A --> D[QuestionnaireListModal]
        A --> E[QuestionnaireBulkModal]
        A --> F[QuestionnaireDownloadModal]
        A --> G[QuestionnaireDeleteModal]
        A --> H[QuestionnaireHistoryList]
    end
    
    subgraph "State Management Layer"
        I[Redux Store] --> J[Questionnaire Reducer]
        I --> K[QuestionnaireByProps Reducer]
        I --> L[QuestionnaireIndex Reducer]
        I --> M[QuestionnaireDetail Reducer]
    end
    
    subgraph "Business Logic Layer"
        N[Redux Saga] --> O[Questionnaire Actions]
        N --> P[Async Operations]
        N --> Q[Side Effects]
    end
    
    subgraph "Data Access Layer"
        R[API Services] --> S[questionnaire.service.ts]
        R --> T[fetchQuestionnaireByProps.ts]
        R --> U[bulkOpsRevamp Services]
        V[Firebase] --> W[Real-time Database]
        V --> X[Authentication]
    end
    
    subgraph "Utility Layer"
        Y[Utils] --> Z[downloadQuestionnaires.ts]
        Y --> AA[deleteQuestionnaire.ts]
        Y --> BB[uploadBulkQuestionnaire.ts]
        Y --> CC[getQuestionnaireIndex.ts]
    end
    
    A --> I
    I --> N
    N --> R
    N --> V
    A --> Y

Layer Responsibilities:

Presentation Layer

  • QuestionnaireManager: Primary container component orchestrating all questionnaire list operations
  • QuestionnaireList: Data table component handling pagination, sorting, and row-level actions
  • Modal Components: Specialized dialogs for clone, delete, download, and bulk upload operations

State Management Layer

  • Redux Store Structure: Normalized state management with separate slices for different data concerns
  • Type Safety: Comprehensive TypeScript definitions ensuring runtime safety
  • Optimistic Updates: Local state management for improved UX during async operations

Business Logic Layer

  • Redux Saga: Generator-based async flow control for complex operations
  • Action Creators: Type-safe action dispatching with payload validation
  • Side Effect Management: Centralized handling of API calls, logging, and error management

Data Access Layer

  • REST API Integration: Standardized HTTP operations with error handling and retry logic
  • Firebase Integration: Real-time data synchronization and authentication
  • Caching Strategy: Session-based caching with TTL for performance optimization

Routes and Navigation

The questionnaire module integrates with the application’s routing system through well-defined route configurations that handle access control, feature flags, and validation.

Primary Routes

RouteComponentPurposeAccess ControlFeature Flag
/admin/questionnairesQuestionnairesPageMain questionnaire list viewADMIN_QUESTIONNAIRE_ALLCUSTOMIZABLE_QUESTIONNAIRE
/admin/questionnaires/editQuestionnaireEditPageQuestionnaire editor interfaceADMIN_QUESTIONNAIRE_ALLCUSTOMIZABLE_QUESTIONNAIRE

Route Configuration (src/routes/admin-routes.js:420-425):

<Route
    exact
    path="/admin/questionnaires"
    component={QuestionnairesPage}
    withValidation
    access={RoleResources.ADMIN_QUESTIONNAIRE_ALL}
    feature={Features.CUSTOMIZABLE_QUESTIONNAIRE}
/>
flowchart TD
    A[Admin Dashboard] --> B[/admin/questionnaires]
    B --> C{User has ADMIN_QUESTIONNAIRE_ALL?}
    C -->|Yes| D{Feature CUSTOMIZABLE_QUESTIONNAIRE enabled?}
    C -->|No| E[Access Denied]
    D -->|Yes| F[QuestionnaireManager]
    D -->|No| G[Feature Disabled]
    F --> H[List View - Published Tab]
    F --> I[List View - Deleted Tab]
    H --> J[Create New] 
    H --> K[Edit Existing]
    H --> L[Clone/Delete Actions]
    H --> M[Bulk Operations]
    J --> N[/admin/questionnaires/edit?questionnaire=new]
    K --> O[/admin/questionnaires/edit?questionnaire=:id]

Access Control Logic:

  • Role Validation: All routes require ADMIN_QUESTIONNAIRE_ALL permission
  • Feature Flags: Routes respect CUSTOMIZABLE_QUESTIONNAIRE feature toggle
  • Validation Middleware: withValidation ensures user session and permissions
  • Organization Context: Some features respect organization-level settings

Core Components

QuestionnaireManager

File: src/components/questionnaires/QuestionnaireManager.js

The QuestionnaireManager serves as the primary orchestration component for all questionnaire list operations. It manages state, coordinates between child components, and handles complex business logic flows.

Key Responsibilities:

  • State Coordination: Manages local component state and Redux store integration
  • Event Orchestration: Handles communication between child components
  • Lifecycle Management: Controls component mounting, updating, and cleanup
  • Permission Enforcement: Implements role-based access control for operations

Component Architecture:

classDiagram
    class QuestionnaireManager {
        +state: ComponentState
        +props: ConnectedProps
        +componentDidMount()
        +componentDidUpdate()
        +handleChangePage(index)
        +handleAddQuestionnaire()
        +handleClickCheckbox(questionnaireID)
        +handleDownloadQuestionnaires()
        +handleConfirmDelete(questionnaireID)
        +setSortType(type)
        +render()
    }
    
    class ComponentState {
        +isLoading: boolean
        +isShowDownloadModal: boolean
        +isSelectAll: boolean
        +questionnaireDownloadList: Object
    }
    
    class ConnectedProps {
        +questionnaires: Questionnaires
        +questionnaireFilter: FilterObject
        +modalShown: string
        +access: PermissionObject
        +organization: Organization
    }
    
    QuestionnaireManager --> ComponentState
    QuestionnaireManager --> ConnectedProps

State Management Details:

interface QuestionnaireManagerState {
    isLoading: boolean;                    // Global loading indicator
    isShowDownloadModal: boolean;          // Download modal visibility
    isSelectAll: boolean;                  // Select all checkbox state
    questionnaireDownloadList: {           // Selected items for download
        [questionnaireId: string]: boolean;
    };
}

Critical Methods:

  1. handleClickCheckbox(questionnaireID: string):

    • Manages multi-select functionality for bulk operations
    • Handles “select all” logic with optimized state updates
    • Maintains referential integrity for selected items
  2. handleDownloadQuestionnaires():

    • Orchestrates bulk download workflow
    • Integrates with downloadSelectedQuestionnaires service
    • Manages success/error states with user feedback
  3. setSortType(type: string):

    • Implements bi-directional sorting logic
    • Coordinates with Redux actions for server-side sorting
    • Maintains sort state across pagination

Connected Redux State (src/components/questionnaires/QuestionnaireManager.js:326-353):

const mapStateToProps = (state) => ({
    auth: state.firebase.auth,
    access: state.userAccess.admin.questionnaire.all.permissions,
    departmentIndex: state.departmentIndex,
    organization: state.organization.organization,
    profile: state.firebase.profile,
    questionnaireFilter: {
        filterQuery: state.questionnaire.filterQuery,
        page: state.questionnaire.page,
        totalItem: state.questionnaire.totalItem,
        // ... additional filter properties
    },
    questionnaires: state.questionnaire.paginateQuestionnaires,
    questionnaireByProps: state.questionnaireByProps,
    // ... additional connected properties
});

QuestionnaireListModal

File: src/components/questionnaires/QuestionnaireListModal.tsx

A sophisticated modal component that handles both clone and delete confirmation workflows with comprehensive error handling and user feedback mechanisms.

Functional Architecture:

stateDiagram-v2
    [*] --> Closed
    Closed --> CloneMode : modalShown = 'clone'
    Closed --> DeleteMode : modalShown = 'delete'
    
    CloneMode --> Processing : handleCloneQuestionnaire()
    DeleteMode --> Processing : handleConfirmDelete()
    
    Processing --> Success : Operation Complete
    Processing --> Error : Operation Failed
    
    Success --> Closed : Auto-dismiss
    Error --> CloneMode : Retry Clone
    Error --> DeleteMode : Retry Delete
    
    CloneMode --> Closed : Cancel
    DeleteMode --> Closed : Cancel

Clone Operation Logic:

const handleCloneQuestionnaire = () => {
    const now = moment().toISOString(true);
    const authId = store.getState().firebase.auth.uid;
    
    const questionnaire: Common.CreateQuestionnaireRequest = {
        title: selectedQuestionnaire.value.title + ' (Copy)',
        tags: selectedQuestionnaire?.value?.tags ?? {},
        dateCreated: now,
        dateUpdated: now,
        modifiedBy: authId,
        questionnaireIndexID: '',
        autoAssignment: selectedQuestionnaire.value.autoAssignment ?? {},
        type: selectedQuestionnaire.value.type,
        status: selectedQuestionnaire.value.status,
        questions: selectedQuestionnaire.value.questions,
    };
    
    dispatch(cloneQuestionnaireAsync.request({
        oldQuestionnaireKey: selectedQuestionnaire.key,
        newQuestionnaireData: questionnaire,
    }));
};

Key Features:

  • Deep Clone Logic: Preserves all questionnaire metadata and structure
  • Conflict Resolution: Handles title conflicts with automatic suffix generation
  • Version Management: Maintains proper version history for cloned items
  • Firebase Integration: Direct Firebase operations for real-time updates

QuestionnaireBulkModal

File: src/components/questionnaires/QuestionnaireBulkModal.tsx

An advanced bulk upload interface that implements a multi-step wizard for Excel-based questionnaire imports with comprehensive validation and error reporting.

Component Class Structure:

classDiagram
    class QuestionnaireBulkModal {
        +state: BulkModalState
        +props: BulkModalPropsExtend
        +componentDidMount()
        +componentDidUpdate()
        +getTagByLabel(label: string)
        +getDepartmentsOptions()
        +handleSelectDepartment(optionValue: string)
        +handleUploadBulk()
        +handleFileChange(event: any)
        +renderFileContainer()
    }
    
    class BulkModalState {
        +title: string
        +file: File | null
        +_isUploadingSingle: boolean
        +_uploadProgress: number
        +questionTag: any
        +questionnaireDepartments: string[]
        +isSuccess: boolean
        +errorData: ErrorData[]
        +errorMessage: string
        +downloadableTemplates: any[]
        +questionnaireTemplate: string
    }

Multi-Step Workflow:

  1. Step 1 - Naming: Questionnaire template name input with validation
  2. Step 2 - Template Download: Template selection and download
  3. Step 3 - Department Selection: Multi-select department assignment
  4. Step 4 - File Upload: Excel file selection with format validation
  5. Step 5 - Processing: Upload execution with progress tracking

File Validation Logic:

handleFileChange = (event: any) => {
    const fileTypes = ['xlsx'];
    if (event.target.files.length && event.target.files.length === 1) {
        const file = event.target.files[0];
        const extension = file.name.split('.').pop().toLowerCase();
        
        const isSuccess = fileTypes.indexOf(extension) > -1;
        if (isSuccess) {
            this.setState({ file: file });
        } else {
            toast.error('Cannot parse the file. Please download the template and try again.');
        }
    }
    
    this.setState({ errorData: [], isSuccess: false, errorMessage: '' });
};

Error Handling System:

  • Line-by-Line Validation: Detailed error reporting with line numbers
  • Format Validation: Excel structure and content validation
  • Duplicate Detection: Title conflict resolution
  • User Feedback: Visual error display with corrective guidance

State Management

The questionnaire module implements a sophisticated Redux-based state management system with multiple specialized reducers handling different aspects of questionnaire data and UI state.

Redux Store Structure

graph TD
    A[Root State] --> B[questionnaire]
    A --> C[questionnaireByProps]
    A --> D[questionnaireIndex]
    A --> E[questionnaireDetail]
    
    B --> F[filterQuery: string]
    B --> G[page: number]
    B --> H[totalItem: number]
    B --> I[sortBy: string]
    B --> J[paginateQuestionnaires: QuestionnaireOrdered]
    B --> K[modalShown: 'clone' | 'delete' | null]
    B --> L[modalBulkShown: boolean]
    B --> M[selectedQuestionnaire: Questionnaire]
    B --> N[tab: 'published' | 'deleted']
    B --> O[isLoadingTable: boolean]
    
    C --> P[data: Questionnaire[]]
    C --> Q[isLoading: boolean]
    C --> R[error: string | null]
    
    D --> S[index: QuestionnaireIndex]
    D --> T[isLoading: boolean]
    
    E --> U[questionnaire: QuestionnaireDetail]
    E --> V[isEditing: boolean]
    E --> W[isDirty: boolean]

Primary Questionnaire Reducer

File: src/reducers/questionnaire/questionnaire.action.ts

State Interface (src/reducers/questionnaire/type.d.ts):

interface QuestionnairesState {
    filterQuery: string;
    sortBy: string;
    page: number;
    totalItem: number;
    status: string;
    title: string;
    createdAt: string;
    dateCreated: string;
    dateUpdated: string;
    questions: string;
    paginateQuestionnaires: QuestionnaireOrdered | null;
    questionnaires: Questionnaires | null;
    modalShown: 'clone' | 'delete' | null;
    modalBulkShown: boolean;
    selectedQuestionnaire: PopulatedQuestionnaireIndex | null;
    index: { [key: string]: QuestionnaireIndex };
    tab: 'published' | 'deleted';
    isLoadingTable: boolean;
    isLoading: boolean;
}

Action Types and Creators:

  1. Pagination Actions:

    export const setPage = createAction(
        'QUESTIONNAIRE_SET_PAGE',
        (page: number) => ({ page })
    );
     
    export const fetchPaginateQuestionnairesAsync = createAsyncAction(
        'FETCH_PAGINATE_QUESTIONNAIRES_REQUEST',
        'FETCH_PAGINATE_QUESTIONNAIRES_SUCCESS',
        'FETCH_PAGINATE_QUESTIONNAIRES_FAILURE'
    )<PaginationRequest, QuestionnaireOrdered, string>();
  2. Sorting Actions:

    export const setSortQuestionnaire = createAction(
        'QUESTIONNAIRE_SET_SORT',
        (sortBy: string, direction: 'asc' | 'desc' = 'asc') => ({
            sortBy,
            [sortBy]: direction
        })
    );
  3. Modal Management Actions:

    export const showQuestionnaireModal = createAction(
        'QUESTIONNAIRE_SHOW_MODAL',
        (modalType: 'clone' | 'delete' | null, questionnaire: PopulatedQuestionnaireIndex | null) => ({
            modalShown: modalType,
            selectedQuestionnaire: questionnaire
        })
    );

QuestionnaireByProps Reducer

File: src/reducers/questionnaireByProps.reducer.ts

Optimized reducer for handling questionnaire data fetching with specific property filtering, designed for performance in large datasets.

State Structure:

interface QuestionnaireByPropsState {
    data: Questionnaire[];
    isLoading: boolean;
    error: string | null;
    lastFetch: number;
    cacheKey: string;
}

Optimization Strategies:

  • Selective Property Loading: Fetches only required questionnaire properties
  • Caching Layer: Implements TTL-based caching to reduce API calls
  • Normalized Data Structure: Prevents data duplication and ensures consistency

Redux Saga Integration

File: src/sagas/questionnaire/questionnaire.actionSaga.ts

Complex async operation management using Redux Saga generator functions for handling side effects, API coordination, and state synchronization.

Key Saga Functions:

  1. Pagination Saga:

    function* fetchPaginateQuestionnairesSaga(
        action: ActionType<typeof fetchPaginateQuestionnairesAsync.request>
    ) {
        try {
            yield put(setIsLoadingTable(true));
            
            const { sortFields, sortDirections } = action.payload;
            const filters = yield select(getQuestionnaireFilters);
            
            const response = yield call(
                questionnaire.service.fetchPaginatedQuestionnaires,
                { ...filters, sortFields, sortDirections }
            );
            
            yield put(fetchPaginateQuestionnairesAsync.success(response));
        } catch (error) {
            yield put(fetchPaginateQuestionnairesAsync.failure(error.message));
        } finally {
            yield put(setIsLoadingTable(false));
        }
    }
  2. Clone Operation Saga:

    function* cloneQuestionnaireSaga(
        action: ActionType<typeof cloneQuestionnaireAsync.request>
    ) {
        try {
            const { oldQuestionnaireKey, newQuestionnaireData } = action.payload;
            
            // Create new questionnaire
            const newQuestionnaire = yield call(
                questionnaire.service.createQuestionnaire,
                newQuestionnaireData
            );
            
            // Update questionnaire index
            yield call(
                questionnaire.service.updateQuestionnaireIndex,
                newQuestionnaire.id,
                { clonedFrom: oldQuestionnaireKey }
            );
            
            // Refresh list data
            yield put(fetchPaginateQuestionnairesAsync.request());
            yield put(dismissQuestionnaireModal());
            
            yield call(toast.success, 'Questionnaire cloned successfully');
        } catch (error) {
            yield call(toast.error, `Clone failed: ${error.message}`);
        }
    }

Saga Coordination Patterns:

  • Race Conditions: Handles concurrent operations with proper cancellation
  • Error Boundaries: Comprehensive error handling with user feedback
  • Optimistic Updates: Local state updates before server confirmation
  • Cache Invalidation: Automatic data refresh after mutations

API Integration

The questionnaire module integrates with multiple API layers including REST endpoints, Firebase real-time database, and specialized bulk operation services.

REST API Endpoints

Base Service File: src/services/questionnaire.service.ts

EndpointMethodPurposeParametersFile Location
/questionnaires/paginatePOSTPaginated questionnaire listingsortFields, sortDirections, filtersquestionnaire.service.ts:45
/questionnaires/:idGETSingle questionnaire retrievalquestionnaireIdquestionnaire.service.ts:78
/questionnaires/minified-by-propsPOSTOptimized questionnaire propertiesproperties[]fetchQuestionnaireByProps.ts:23
/questionnairesPOSTCreate new questionnaireCreateQuestionnaireRequestquestionnaire.service.ts:102
/questionnaires/:idDELETEDelete questionnairequestionnaireIdutils/deleteQuestionnaire.ts:15
/questionnaires/bulk-uploadPOSTBulk Excel uploadFormData with fileutils/uploadBulkQuestionnaire.ts:67
/questionnaires/template/:templateNameGETDownload Excel templatetemplateNameQuestionnaireBulkModal.tsx:163
/bulk-edit/downloadPOSTBulk download for editingquestionnaireIds[]bulkDownloadQuestionnaire.service.ts:34
/vertex-ai/imagevalidation/recommend-keywordsPOSTAI keyword recommendationstitle, descriptionquestionnaire.service.ts:89

Firebase Database Integration

Database Paths:

  • /questionnaireIndex/{organizationId}/{questionnaireIndexId} - Questionnaire metadata and versioning
  • /questionnaire/{organizationId}/{questionnaireId} - Full questionnaire content and structure

Firebase Operations:

  1. Real-time Updates: Live synchronization of questionnaire changes across sessions
  2. Soft Delete Implementation: Flag-based deletion with disabled: true field
  3. Version Management: Automatic version tracking with timestamp metadata
  4. Authentication Integration: Firebase Auth token validation for all operations

Firebase Service Implementation:

// QuestionnaireListModal.tsx:78-92
const selectedRef = `/questionnaire/${profile.organization}/${latestVersion}`;
firebase
    .update(selectedRef, { disabled: true })
    .then(() => {
        const questionnaireIndexRef = `/questionnaireIndex/${profile.organization}/${selectedQuestionnaire.key}`;
        return firebase.update(questionnaireIndexRef, { disabled: true });
    })
    .then(() => {
        handleCloseModal();
    })
    .catch((err) => {
        handleCloseModal();
    });

Bulk Operations Service

File: src/services/bulkOpsRevamp/bulkDownloadQuestionnaire.service.ts

Specialized service handling bulk questionnaire operations with optimized payload processing and file generation.

Download Service Architecture:

export const downloadSelectedQuestionnaires = async (questionnaireIds: string[]) => {
    try {
        const token = await getCurrentAuthToken();
        const response = await fetch(`${bulkOpsUrl}/bulk-edit/download`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
            },
            body: JSON.stringify({
                questionnaireIds,
                format: 'excel',
                includeMetadata: true
            })
        });
        
        if (response.ok) {
            const blob = await response.blob();
            const downloadUrl = window.URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = downloadUrl;
            link.download = `questionnaires_${Date.now()}.xlsx`;
            link.click();
            
            return { success: true };
        } else {
            throw new Error(`Download failed: ${response.statusText}`);
        }
    } catch (error) {
        return { success: false, error: error.message };
    }
};

Key Features:

  • Streaming Downloads: Efficient handling of large questionnaire datasets
  • Format Flexibility: Support for multiple export formats (Excel, JSON, CSV)
  • Metadata Inclusion: Optional questionnaire metadata in exports
  • Progress Tracking: Real-time download progress feedback

API Authentication & Error Handling

Authentication Flow:

  1. Firebase Auth token extraction from Redux store
  2. JWT token conversion for backend API compatibility
  3. Automatic token refresh with session management
  4. Role-based permission validation

Error Handling Strategy:

  • Network Resilience: Automatic retry logic for transient failures
  • User Feedback: Toast notifications for operation status
  • Graceful Degradation: Fallback behaviors for service unavailability
  • Logging Integration: Comprehensive error logging for debugging

Package Dependencies

The questionnaire module leverages a comprehensive set of packages and libraries to implement its sophisticated functionality. Below is a detailed analysis of key dependencies and their specific roles within the module.

Core React Ecosystem

PackageVersionPurposeUsage in Questionnaire Module
react16.13.1Core React libraryComponent rendering, hooks, lifecycle management
react-redux7.2.0Redux React bindingsState management integration, connect HOC usage
redux4.0.5State managementCentral store for questionnaire data and UI state
redux-saga^1.1.3Side effect managementAsync operations, API calls, complex business logic
connected-react-router6.8.0Router-Redux integrationNavigation state management, programmatic routing
react-router-dom5.1.2Client-side routingRoute definitions, navigation, parameter handling

React Integration Details:

  • Component Architecture: Class and functional components with hooks
  • State Management: Redux integration via connect HOC and useSelector/useDispatch hooks
  • Lifecycle Management: componentDidMount, componentDidUpdate for data fetching coordination
  • Performance Optimization: React.memo, useMemo, and useCallback for render optimization

UI and Styling Libraries

PackageVersionPurposeUsage in Questionnaire Module
styled-components5.1.0CSS-in-JS stylingComponent styling, theme management, responsive design
react-select3.1.0Advanced select componentsDepartment selection, template chooser in bulk modal
react-beautiful-dnd^13.1.1Drag and drop functionalityQuestion reordering in questionnaire editor
@radix-ui/themes3.1.1Modern UI primitivesModal overlays, dropdowns, advanced UI components
@radix-ui/react-popover1.1.1Popover componentsAction menus, context-sensitive operations

Styled Components Architecture:

// Example from QuestionnaireManager.js
const AdaptiveWrapper = styled(PageWrapper)`
    flex: 1;
    
    @media (min-width: 992px) {
        background-color: #fff;
    }
`;
 
const Tab = styled.span`
    font-size: 12px;
    line-height: 18px;
    color: ${(props) => (props.activeTab ? '#574fcf' : '#A0A4A8')};
    font-weight: 600;
    border-bottom: ${(props) => (props.activeTab ? 'solid 2px #574fcf' : 'none')};
    cursor: pointer;
`;

Data Processing and Utilities

PackageVersionPurposeUsage in Questionnaire Module
lodash^4.17.21Utility functionsObject manipulation, array processing, deep cloning
moment2.24.0Date/time manipulationTimestamp generation, date formatting, timezone handling
moment-timezone0.5.26Timezone supportMulti-timezone date handling for global organizations
xlsx^0.16.2Excel file processingBulk upload file parsing, template generation
papaparse5.2.0CSV parsingAlternative file format support

Lodash Usage Patterns:

// Deep cloning for state management
import cloneDeep from 'lodash/cloneDeep';
let list = cloneDeep(this.state.questionnaireDownloadList);
 
// Object manipulation
import isEqual from 'lodash/isEqual';
if (!isEqual(prevProps.questionTag, this.props.questionTag)) {
    this.setState(this.props.questionTag);
}
 
// Undefined checking
import isUndefined from 'lodash/isUndefined';
if (!isUndefined(organization.allowRecommendations)) {
    // Configuration logic
}

Firebase Integration Stack

PackageVersionPurposeUsage in Questionnaire Module
@firebase/app^0.4.19Firebase coreApplication initialization, configuration
@firebase/database^0.5.7Realtime DatabaseLive questionnaire synchronization
@firebase/auth^0.12.1AuthenticationUser authentication, token management
@firebase/firestore^1.6.1Cloud FirestoreDocument-based data storage
react-redux-firebase3.3.1Firebase-Redux bindingState integration, auth state management

Firebase Integration Architecture:

// Authentication integration
const firebase = useFirebase();
const profile = useSelector((state: RootState) => state.firebase.profile);
 
// Real-time database operations
const selectedRef = `/questionnaire/${profile.organization}/${latestVersion}`;
firebase.update(selectedRef, { disabled: true });
 
// Firestore document operations
const questionnaireIndexRef = `/questionnaireIndex/${profile.organization}/${selectedQuestionnaire.key}`;
firebase.update(questionnaireIndexRef, { disabled: true });

Form Management and Validation

PackageVersionPurposeUsage in Questionnaire Module
formik^2.2.9Form state managementComplex form handling in questionnaire editor
yup0.32.11Schema validationForm validation rules, data type validation
react-hook-form6Performant form libraryOptimized form handling for large questionnaires
@hookform/resolvers1.3.7Validation resolversYup schema integration with react-hook-form

Internationalization Support

PackageVersionPurposeUsage in Questionnaire Module
i18next^17.0.8Internationalization frameworkMulti-language support
react-i18next^10.13.1React i18n integrationTranslation hooks, HOCs
i18next-http-backend^1.0.21Translation loadingDynamic translation file loading

Translation Implementation:

// Hook-based translation
const { t } = useTranslation();
 
// Component-based translation
<Translation>
    {(t) => (
        <div>
            {t('pageQuestionnaire.modalList.titleClone')}
        </div>
    )}
</Translation>
 
// Parameterized translations
<Trans i18nKey="pageQuestionnaire.modalList.warning" />

Development and Testing Tools

PackageVersionPurposeUsage in Questionnaire Module
@testing-library/react^9.3.0React component testingUnit tests for questionnaire components
@types/react16.9.34TypeScript React typesType safety for React components
@types/lodash^4.14.144Lodash TypeScript typesType-safe utility function usage
typescript3Static type checkingCompile-time type validation

Nimbly-Specific Dependencies

PackageVersionPurposeUsage in Questionnaire Module
@nimbly-technologies/nimbly-common1.95.3Shared business typesQuestionnaire data models, API interfaces
@nimbly-technologies/audit-component1.1.8Reusable UI componentsSpecialized audit-related components

Common Types Usage:

import * as Common from '@nimbly-technologies/nimbly-common';
 
// Type-safe questionnaire creation
const questionnaire: Common.CreateQuestionnaireRequest = {
    title: selectedQuestionnaire.value.title + ' (Copy)',
    tags: newTags,
    dateCreated: now,
    dateUpdated: now,
    modifiedBy: authId,
    questionnaireIndexID: '',
    autoAssignment: newAutoAssignment,
    type: selectedQuestionnaire.value.type,
    status: selectedQuestionnaire.value.status,
    questions: selectedQuestionnaire.value.questions,
};

Performance and Monitoring

PackageVersionPurposeUsage in Questionnaire Module
@sentry/react^6.4.1Error monitoringProduction error tracking, performance monitoring
react-ga^2.7.0Google AnalyticsUser interaction tracking, feature usage analytics
@tanstack/react-query4Data fetching/cachingOptimized API call management

Analytics Integration:

// User action tracking
ReactGA.event({
    category: 'Administration',
    action: 'Questionnaire - Bulk Upload',
});
 
// Performance monitoring with Sentry
Sentry.addBreadcrumb({
    category: 'questionnaire',
    message: 'Questionnaire cloned successfully',
    level: 'info',
});

Core Functionality

Listing Functionality

The questionnaire listing functionality provides a comprehensive interface for viewing, searching, filtering, and managing questionnaires within the system. This section details the implementation architecture, data flow, and technical specifications.

Architecture Overview

flowchart TD
    A[QuestionnaireManager] --> B[QuestionnaireListHeader]
    A --> C[QuestionnaireList]
    A --> D[Tab Navigation]
    
    B --> E[Search Interface]
    B --> F[Filter Controls]
    B --> G[Action Buttons]
    
    C --> H[Data Table]
    C --> I[Pagination]
    C --> J[Sorting]
    C --> K[Row Actions]
    
    D --> L[Published Tab]
    D --> M[Deleted Tab]
    
    H --> N[Checkbox Selection]
    H --> O[Questionnaire Row]
    H --> P[Status Indicators]
    
    subgraph "Data Sources"
        Q[Redux Store]
        R[API Service]
        S[Firebase Realtime]
    end
    
    A --> Q
    Q --> R
    Q --> S

Data Flow Implementation

1. Initial Data Loading

The listing functionality begins with component initialization in QuestionnaireManager.componentDidMount():

// QuestionnaireManager.js:47-50
componentDidMount() {
    this.props.fetchPaginateQuestionnairesAsync({ 
        sortFields: 'title', 
        sortDirections: 'asc' 
    });
    this.props.getQuestionnairesByPropsAsync();
}

Data Loading Sequence:

  1. Primary Load: fetchPaginateQuestionnairesAsync loads paginated questionnaire list
  2. Optimized Load: getQuestionnairesByPropsAsync loads minified questionnaire properties
  3. State Synchronization: Redux saga coordinates both data sources
  4. UI Update: Components re-render with loaded data

2. Pagination System

Implementation Details (QuestionnaireManager.js:103-105):

handleChangePage = (index) => {
    this.props.setPage(index);
};

Pagination Logic Flow:

sequenceDiagram
    participant U as User
    participant C as Component
    participant R as Redux
    participant S as Saga
    participant A as API
    
    U->>C: Click page number
    C->>R: dispatch setPage(index)
    R->>R: Update page in state
    R->>S: Trigger componentDidUpdate
    S->>A: fetchPaginateQuestionnaires
    A->>S: Return paginated data
    S->>R: Update questionnaires state
    R->>C: Re-render with new data

Pagination State Management:

  • Current Page: Maintained in state.questionnaire.page
  • Total Items: Tracked in state.questionnaire.totalItem
  • Page Size: Configurable through API parameters
  • Navigation: Previous/next buttons with page number display

3. Sorting Implementation

Multi-Column Sorting Logic (QuestionnaireManager.js:184-192):

setSortType = (type) => {
    const { questionnaireSortBy, questionnaireFilter, setSortQuestionnaire } = this.props;
    
    if (questionnaireSortBy === type) {
        // Toggle sort direction if same column
        setSortQuestionnaire(type, questionnaireFilter[type] === 'asc' ? 'desc' : 'asc');
    } else {
        // Set new sort column with default ascending
        setSortQuestionnaire(type);
    }
};

Sortable Columns:

  • Title: Alphabetical sorting with case-insensitive comparison
  • Created Date: Chronological sorting with timestamp precision
  • Updated Date: Last modification timestamp sorting
  • Questions: Numerical sorting by question count
  • Status: Categorical sorting (Published, Draft, Archived)

Server-Side Sorting Integration:

// Component update trigger for sorting changes
if (questionnaireFilter.sortBy !== sortBy) {
    if (sortBy === 'questions') {
        fetchPaginateQuestionnairesAsync({
            sortFields: 'questionCount',
            sortDirections: this.props.questionnaireFilter[sortBy],
        });
    } else {
        fetchPaginateQuestionnairesAsync({
            sortFields: sortBy,
            sortDirections: this.props.questionnaireFilter[sortBy],
        });
    }
}

4. Tab Navigation System

Tab Implementation (QuestionnaireManager.js:194-212):

_renderTabs = () => {
    const { setTab, activeTab, organization } = this.props;
    const { t } = useTranslation();
    
    return (
        <Tabs>
            <Tab 
                activeTab={activeTab === 'published'} 
                onClick={() => setTab('published')} 
                id="tab_pub_quest"
            >
                {t('nav.published')}
            </Tab>
            {organization && 
            (!organization.hasOwnProperty('displayBlockedTab') ||
             (organization.hasOwnProperty('displayBlockedTab') && organization.displayBlockedTab)) ? (
                <Tab 
                    activeTab={activeTab === 'deleted'} 
                    onClick={() => setTab('deleted')} 
                    id="tab_del_quest"
                >
                    {t('nav.deleted')}
                </Tab>
            ) : null}
        </Tabs>
    );
};

Tab Functionality:

  • Published Tab: Shows active questionnaires available for use
  • Deleted Tab: Shows soft-deleted questionnaires (recovery possible)
  • Organization Control: Tab visibility controlled by organization settings
  • Permission-Based: Tab access respects user role permissions

5. Selection and Bulk Operations

Multi-Select Implementation (QuestionnaireManager.js:111-135):

handleClickCheckbox = (questionnaireID) => {
    let selectAllValue = false;
    this.setState({ isLoading: true });
    let list = cloneDeep(this.state.questionnaireDownloadList);
    
    if (questionnaireID === 'all') {
        if (this.state.isSelectAll === true) {
            // Deselect all
            list = {};
        } else {
            // Select all visible items
            const qIndex = {};
            this.props.questionnaireByProps.data.forEach((questionnaire) => {
                qIndex[questionnaire.questionnaireIndexID] = true;
            });
            list = qIndex;
            selectAllValue = true;
        }
    } else if (!list[questionnaireID]) {
        // Select individual item
        list[questionnaireID] = true;
    } else {
        // Deselect individual item
        delete list[questionnaireID];
        selectAllValue = false;
    }
    
    this.setState({ 
        questionnaireDownloadList: list, 
        isLoading: false, 
        isSelectAll: selectAllValue 
    });
};

Selection State Management:

  • Individual Selection: Checkbox per questionnaire row
  • Select All: Master checkbox for current page
  • State Persistence: Selection maintained during pagination
  • Visual Feedback: Selected rows highlighted with checkmark icons

Performance Optimizations

1. Virtual Scrolling: Large lists rendered efficiently with react-virtualized 2. Memoization: Component re-render optimization with React.memo 3. Debounced Search: Search input debounced to reduce API calls 4. Lazy Loading: Images and large content loaded on demand 5. Caching: Session-based caching for frequently accessed data

Accessibility Features

1. ARIA Labels: Screen reader support for all interactive elements 2. Keyboard Navigation: Full keyboard accessibility for table navigation 3. Focus Management: Proper focus handling for modal interactions 4. Color Contrast: WCAG compliant contrast ratios for visual elements 5. Screen Reader Support: Semantic HTML structure for assistive technologies


Copy/Clone Functionality

The questionnaire cloning system provides sophisticated duplication capabilities with metadata preservation, conflict resolution, and version management. This functionality enables users to create questionnaire templates and variations efficiently.

Clone Operation Architecture

flowchart TD
    A[User Clicks Clone] --> B[QuestionnaireListModal]
    B --> C[Clone Confirmation Dialog]
    C --> D[User Confirms]
    D --> E[handleCloneQuestionnaire]
    
    E --> F[Prepare Clone Data]
    F --> G[Generate New Metadata]
    G --> H[Preserve Original Structure]
    H --> I[Create Clone Request]
    
    I --> J[Dispatch cloneQuestionnaireAsync]
    J --> K[Redux Saga Processing]
    K --> L[API Service Call]
    L --> M[Database Operations]
    
    M --> N[Create New Questionnaire]
    N --> O[Update Index]
    O --> P[Refresh List Data]
    P --> Q[Update UI State]
    Q --> R[Show Success Notification]
    
    subgraph "Error Handling"
        S[Validation Errors]
        T[Network Errors]
        U[Database Conflicts]
        V[Permission Errors]
    end
    
    K --> S
    L --> T
    M --> U
    N --> V

Clone Data Structure Preparation

Source Data Analysis (QuestionnaireListModal.tsx:46-68):

const handleCloneQuestionnaire = () => {
    if (isLoading) {
        return;
    }
    
    const now = moment().toISOString(true);
    const authId = store.getState().firebase.auth.uid;
    const newTags = selectedQuestionnaire?.value?.tags ?? {};
    const newAutoAssignment = selectedQuestionnaire?.value?.autoAssignment ?? {};
    
    const questionnaire: Common.CreateQuestionnaireRequest = {
        title: selectedQuestionnaire.value.title + ' (Copy)',
        tags: newTags,
        dateCreated: now,
        dateUpdated: now,
        modifiedBy: authId,
        questionnaireIndexID: '',
        autoAssignment: newAutoAssignment,
        type: selectedQuestionnaire.value.type,
        status: selectedQuestionnaire.value.status,
        questions: selectedQuestionnaire.value.questions,
    };
    
    dispatch(cloneQuestionnaireAsync.request({
        oldQuestionnaireKey: selectedQuestionnaire.key,
        newQuestionnaireData: questionnaire,
    }));
};

Deep Clone Implementation Details

1. Metadata Preservation

The clone operation preserves critical questionnaire metadata while generating new identifiers and timestamps:

interface CloneMetadata {
    // Preserved from original
    title: string;                    // Modified with " (Copy)" suffix
    tags: QuestionnaireTag[];        // Deep cloned tag structure
    autoAssignment: AutoAssignment;   // Preserved assignment rules
    type: QuestionnaireType;         // Questionnaire category
    status: QuestionnaireStatus;     // Publication status
    questions: Question[];           // Complete question tree
    
    // Generated for clone
    dateCreated: string;             // Current timestamp
    dateUpdated: string;             // Current timestamp
    modifiedBy: string;              // Current user ID
    questionnaireIndexID: string;    // New unique identifier
}

2. Question Structure Cloning

Questions undergo deep cloning to ensure complete independence from the source questionnaire:

// Deep clone process for questions
const cloneQuestions = (originalQuestions: Question[]): Question[] => {
    return originalQuestions.map(question => ({
        ...question,
        id: generateNewQuestionId(),
        children: question.children ? cloneQuestions(question.children) : [],
        conditionalLogic: question.conditionalLogic ? {
            ...question.conditionalLogic,
            conditions: question.conditionalLogic.conditions.map(condition => ({
                ...condition,
                id: generateNewConditionId()
            }))
        } : null,
        validationRules: question.validationRules ? {
            ...question.validationRules,
            customRules: question.validationRules.customRules?.map(rule => ({
                ...rule,
                id: generateNewRuleId()
            }))
        } : null
    }));
};

3. Tag System Cloning

The tag system requires special handling to maintain relationships while creating new instances:

interface QuestionnaireTag {
    id: string;
    label: string;
    color: string;
    category: string;
    isSystemTag: boolean;
    permissions: TagPermissions;
}
 
const cloneTags = (originalTags: QuestionnaireTag[]): QuestionnaireTag[] => {
    return originalTags.map(tag => ({
        ...tag,
        id: tag.isSystemTag ? tag.id : generateNewTagId(), // Preserve system tags
        permissions: {
            ...tag.permissions,
            createdBy: getCurrentUserId(),
            createdAt: new Date().toISOString()
        }
    }));
};

Redux Saga Clone Processing

Saga Implementation (questionnaire.actionSaga.ts):

function* cloneQuestionnaireSaga(
    action: ActionType<typeof cloneQuestionnaireAsync.request>
) {
    try {
        const { oldQuestionnaireKey, newQuestionnaireData } = action.payload;
        
        // Validate clone permissions
        const permissions = yield select(getQuestionnairePermissions);
        if (!permissions.canCreate) {
            throw new Error('Insufficient permissions to clone questionnaire');
        }
        
        // Check for title conflicts
        const existingQuestionnaires = yield select(getQuestionnaires);
        const titleConflict = existingQuestionnaires.find(q => 
            q.title === newQuestionnaireData.title
        );
        
        if (titleConflict) {
            newQuestionnaireData.title = generateUniqueTitle(
                newQuestionnaireData.title, 
                existingQuestionnaires
            );
        }
        
        // Create questionnaire index entry
        const questionnaireIndex = yield call(
            createQuestionnaireIndex,
            {
                ...newQuestionnaireData,
                clonedFrom: oldQuestionnaireKey,
                cloneTimestamp: new Date().toISOString()
            }
        );
        
        // Create questionnaire content
        const questionnaire = yield call(
            createQuestionnaire,
            {
                ...newQuestionnaireData,
                questionnaireIndexID: questionnaireIndex.id
            }
        );
        
        // Update clone relationship
        yield call(
            updateQuestionnaireIndex,
            questionnaireIndex.id,
            {
                questionnaireId: questionnaire.id,
                status: 'cloned_successfully'
            }
        );
        
        // Refresh data sources
        yield put(fetchPaginateQuestionnairesAsync.request());
        yield put(getQuestionnairesByPropsAsync.request());
        
        // Close modal and show success
        yield put(dismissQuestionnaireModal());
        yield call(toast.success, 'Questionnaire cloned successfully');
        
        // Analytics tracking
        yield call(ReactGA.event, {
            category: 'Administration',
            action: 'Questionnaire - Clone',
            label: newQuestionnaireData.title
        });
        
    } catch (error) {
        yield call(toast.error, `Clone failed: ${error.message}`);
        yield put(cloneQuestionnaireAsync.failure(error.message));
    }
}

Conflict Resolution System

1. Title Conflict Resolution

Automatic title disambiguation when duplicate names are detected:

const generateUniqueTitle = (baseTitle: string, existingQuestionnaires: Questionnaire[]): string => {
    const baseName = baseTitle.replace(/ \(Copy\)$/, '').replace(/ \(Copy \d+\)$/, '');
    let counter = 1;
    let newTitle = `${baseName} (Copy)`;
    
    while (existingQuestionnaires.some(q => q.title === newTitle)) {
        counter++;
        newTitle = `${baseName} (Copy ${counter})`;
    }
    
    return newTitle;
};

2. ID Conflict Prevention

Systematic ID regeneration to prevent database conflicts:

const regenerateIds = (questionnaire: Questionnaire): Questionnaire => {
    const idMap = new Map<string, string>();
    
    const generateNewId = (oldId: string, prefix: string): string => {
        if (idMap.has(oldId)) {
            return idMap.get(oldId)!;
        }
        
        const newId = `${prefix}_${generateUUID()}`;
        idMap.set(oldId, newId);
        return newId;
    };
    
    return {
        ...questionnaire,
        id: generateNewId(questionnaire.id, 'q'),
        questions: questionnaire.questions.map(question => regenerateQuestionIds(question, generateNewId))
    };
};

Version Management Integration

1. Clone Lineage Tracking

Maintaining genealogy information for audit and management purposes:

interface CloneLineage {
    originalId: string;
    cloneId: string;
    cloneDepth: number;
    cloneTimestamp: string;
    clonedBy: string;
    cloneReason?: string;
    parentClones: string[];
    childClones: string[];
}
 
const establishCloneLineage = (original: Questionnaire, clone: Questionnaire): CloneLineage => {
    return {
        originalId: original.id,
        cloneId: clone.id,
        cloneDepth: (original.cloneDepth || 0) + 1,
        cloneTimestamp: new Date().toISOString(),
        clonedBy: getCurrentUserId(),
        parentClones: original.parentClones ? [...original.parentClones, original.id] : [original.id],
        childClones: []
    };
};

2. Clone Relationship Management

Bidirectional relationship tracking for clone family management:

const updateCloneRelationships = async (originalId: string, cloneId: string) => {
    // Update original questionnaire with new clone reference
    await updateQuestionnaire(originalId, {
        childClones: [...(original.childClones || []), cloneId]
    });
    
    // Update clone with parent reference
    await updateQuestionnaire(cloneId, {
        parentClones: [...(original.parentClones || []), originalId],
        cloneDepth: (original.cloneDepth || 0) + 1
    });
    
    // Update all siblings with new family member
    if (original.childClones) {
        for (const siblingId of original.childClones) {
            await updateQuestionnaire(siblingId, {
                siblingClones: [...(sibling.siblingClones || []), cloneId]
            });
        }
    }
};

Clone Performance Optimization

1. Batch Operations

Optimized database operations for large questionnaire cloning:

const optimizedCloneCreation = async (cloneData: CloneRequest): Promise<Questionnaire> => {
    const batch = firebase.batch();
    
    // Batch all related document creation
    const questionnaireRef = firebase.collection('questionnaires').doc();
    const indexRef = firebase.collection('questionnaireIndex').doc();
    
    batch.set(questionnaireRef, cloneData.questionnaire);
    batch.set(indexRef, cloneData.index);
    
    // Batch question document creation
    cloneData.questions.forEach(question => {
        const questionRef = firebase.collection('questions').doc();
        batch.set(questionRef, question);
    });
    
    await batch.commit();
    
    return { id: questionnaireRef.id, ...cloneData.questionnaire };
};

2. Progressive Clone Loading

Large questionnaires cloned with progress indication:

const progressiveClone = async (
    originalId: string, 
    onProgress: (progress: number) => void
): Promise<Questionnaire> => {
    const steps = ['metadata', 'questions', 'validations', 'relationships'];
    let completedSteps = 0;
    
    for (const step of steps) {
        await executeCloneStep(step, originalId);
        completedSteps++;
        onProgress((completedSteps / steps.length) * 100);
    }
    
    return getClonedQuestionnaire();
};

Delete Functionality

The questionnaire deletion system implements a sophisticated soft-delete mechanism with recovery capabilities, audit trails, and cascading relationship management. This approach ensures data integrity while providing safety mechanisms for accidental deletions.

Delete Operation Architecture

flowchart TD
    A[User Initiates Delete] --> B[Delete Confirmation Modal]
    B --> C[Permission Validation]
    C --> D{Has Delete Permission?}
    D -->|No| E[Access Denied Message]
    D -->|Yes| F[Dependency Check]
    
    F --> G{Has Active Dependencies?}
    G -->|Yes| H[Show Dependency Warning]
    G -->|No| I[Soft Delete Process]
    
    H --> J[User Confirmation]
    J --> K{Force Delete?}
    K -->|No| L[Cancel Operation]
    K -->|Yes| I
    
    I --> M[Update Questionnaire Status]
    M --> N[Update Index Status]
    N --> O[Update Related Entities]
    O --> P[Create Audit Log]
    P --> Q[Refresh UI Data]
    Q --> R[Show Success Message]
    
    subgraph "Error Handling"
        S[Database Errors]
        T[Network Failures]
        U[Validation Errors]
        V[Permission Errors]
    end
    
    M --> S
    N --> T
    O --> U
    C --> V

Soft Delete Implementation

Primary Delete Handler (QuestionnaireListModal.tsx:71-93):

const handleConfirmDelete = () => {
    if (isLoading) {
        return;
    }
    
    const { versions } = questionnaireIndex[selectedQuestionnaire.key];
    const latestVersion = versions[versions.length - 1];
    const selectedRef = `/questionnaire/${profile.organization}/${latestVersion}`;
    
    // ROLE-QUESTIONNAIRE-DELETE
    firebase
        .update(selectedRef, { disabled: true })
        .then(() => {
            const questionnaireIndexRef = `/questionnaireIndex/${profile.organization}/${selectedQuestionnaire.key}`;
            return firebase.update(questionnaireIndexRef, { disabled: true });
        })
        .then(() => {
            handleCloseModal();
        })
        .catch((err) => {
            // Error handling
            handleCloseModal();
        });
};

Advanced Delete Utility

Enhanced Delete Service (utils/deleteQuestionnaire.ts):

const deleteQuestionnaire = async (
    questionnaireID: string,
    onSuccess: (id: string) => void
): Promise<void> => {
    try {
        // Validate deletion permissions
        const permissions = await getQuestionnairePermissions();
        if (!permissions.canDelete) {
            throw new Error('Insufficient permissions to delete questionnaire');
        }
        
        // Check for active dependencies
        const dependencies = await checkQuestionnaireDependencies(questionnaireID);
        if (dependencies.hasActiveDependencies) {
            const confirmForceDelete = await showDependencyWarning(dependencies);
            if (!confirmForceDelete) {
                return;
            }
        }
        
        // Execute soft delete transaction
        await executeTransactionalDelete(questionnaireID);
        
        // Update local state
        onSuccess(questionnaireID);
        
        // Show success notification
        toast.success('Questionnaire deleted successfully');
        
        // Track analytics
        ReactGA.event({
            category: 'Administration',
            action: 'Questionnaire - Delete',
            label: questionnaireID
        });
        
    } catch (error) {
        console.error('Delete operation failed:', error);
        toast.error(`Delete failed: ${error.message}`);
    }
};

Dependency Management System

1. Dependency Detection

Comprehensive analysis of questionnaire relationships before deletion:

interface QuestionnaireDependencies {
    hasActiveDependencies: boolean;
    activeSubmissions: number;
    linkedSites: string[];
    childQuestionnaires: string[];
    scheduledAssignments: ScheduledAssignment[];
    reportReferences: ReportReference[];
    templateUsages: TemplateUsage[];
}
 
const checkQuestionnaireDependencies = async (
    questionnaireID: string
): Promise<QuestionnaireDependencies> => {
    const [
        submissions,
        sites,
        children,
        assignments,
        reports,
        templates
    ] = await Promise.all([
        getActiveSubmissions(questionnaireID),
        getLinkedSites(questionnaireID),
        getChildQuestionnaires(questionnaireID),
        getScheduledAssignments(questionnaireID),
        getReportReferences(questionnaireID),
        getTemplateUsages(questionnaireID)
    ]);
    
    return {
        hasActiveDependencies: submissions.length > 0 || sites.length > 0 || children.length > 0,
        activeSubmissions: submissions.length,
        linkedSites: sites.map(s => s.id),
        childQuestionnaires: children.map(c => c.id),
        scheduledAssignments: assignments,
        reportReferences: reports,
        templateUsages: templates
    };
};

2. Cascading Delete Logic

Controlled propagation of delete operations through related entities:

const executeCascadingDelete = async (
    questionnaireID: string,
    options: CascadeDeleteOptions
): Promise<void> => {
    const deleteOperations: Promise<void>[] = [];
    
    if (options.deleteSubmissions) {
        // Soft delete all related submissions
        const submissions = await getQuestionnaireSubmissions(questionnaireID);
        submissions.forEach(submission => {
            deleteOperations.push(softDeleteSubmission(submission.id));
        });
    }
    
    if (options.unlinkSites) {
        // Remove questionnaire from site assignments
        const sites = await getLinkedSites(questionnaireID);
        sites.forEach(site => {
            deleteOperations.push(unlinkQuestionnaireFromSite(site.id, questionnaireID));
        });
    }
    
    if (options.deleteChildren) {
        // Recursively delete child questionnaires
        const children = await getChildQuestionnaires(questionnaireID);
        children.forEach(child => {
            deleteOperations.push(deleteQuestionnaire(child.id, () => {}));
        });
    }
    
    // Execute all operations concurrently
    await Promise.all(deleteOperations);
};

Transaction-Safe Delete Implementation

Database Transaction Management:

const executeTransactionalDelete = async (questionnaireID: string): Promise<void> => {
    const batch = firebase.batch();
    
    try {
        // Get questionnaire references
        const questionnaireRef = firebase
            .collection('questionnaires')
            .doc(questionnaireID);
        
        const indexRef = firebase
            .collection('questionnaireIndex')
            .doc(questionnaireID);
        
        // Prepare delete timestamps
        const deleteTimestamp = new Date().toISOString();
        const deletedBy = getCurrentUserId();
        
        // Update questionnaire document
        batch.update(questionnaireRef, {
            disabled: true,
            deletedAt: deleteTimestamp,
            deletedBy: deletedBy,
            deleteReason: 'user_initiated'
        });
        
        // Update index document
        batch.update(indexRef, {
            disabled: true,
            deletedAt: deleteTimestamp,
            deletedBy: deletedBy,
            status: 'deleted'
        });
        
        // Update related questions
        const questions = await getQuestionnaireQuestions(questionnaireID);
        questions.forEach(question => {
            const questionRef = firebase.collection('questions').doc(question.id);
            batch.update(questionRef, {
                disabled: true,
                deletedAt: deleteTimestamp
            });
        });
        
        // Commit transaction
        await batch.commit();
        
        // Create audit log entry
        await createAuditLogEntry({
            action: 'questionnaire_deleted',
            resourceId: questionnaireID,
            timestamp: deleteTimestamp,
            userId: deletedBy,
            metadata: {
                questionnaireName: questionnaire.title,
                questionCount: questions.length
            }
        });
        
    } catch (error) {
        console.error('Transaction failed:', error);
        throw new Error(`Delete transaction failed: ${error.message}`);
    }
};

Recovery System

1. Deleted Item Management

Comprehensive interface for managing deleted questionnaires:

interface DeletedQuestionnaireView {
    id: string;
    title: string;
    deletedAt: string;
    deletedBy: string;
    deleteReason: string;
    canRestore: boolean;
    dependencyStatus: 'safe' | 'conflicts' | 'unknown';
    retentionPeriod: number; // days until permanent deletion
}
 
const getDeletedQuestionnaires = async (): Promise<DeletedQuestionnaireView[]> => {
    const deletedItems = await firebase
        .collection('questionnaires')
        .where('disabled', '==', true)
        .where('deletedAt', '!=', null)
        .orderBy('deletedAt', 'desc')
        .get();
    
    return deletedItems.docs.map(doc => {
        const data = doc.data();
        return {
            id: doc.id,
            title: data.title,
            deletedAt: data.deletedAt,
            deletedBy: data.deletedBy,
            deleteReason: data.deleteReason || 'unknown',
            canRestore: canRestoreQuestionnaire(data),
            dependencyStatus: assessRestoreConflicts(data),
            retentionPeriod: calculateRetentionPeriod(data.deletedAt)
        };
    });
};

2. Restore Functionality

Safe restoration with conflict detection and resolution:

const restoreQuestionnaire = async (
    questionnaireID: string
): Promise<RestoreResult> => {
    try {
        // Validate restore permissions
        const permissions = await getQuestionnairePermissions();
        if (!permissions.canRestore) {
            throw new Error('Insufficient permissions to restore questionnaire');
        }
        
        // Check for restoration conflicts
        const conflicts = await checkRestoreConflicts(questionnaireID);
        if (conflicts.hasConflicts) {
            return {
                success: false,
                conflicts: conflicts,
                requiresResolution: true
            };
        }
        
        // Execute restore transaction
        const batch = firebase.batch();
        
        const questionnaireRef = firebase.collection('questionnaires').doc(questionnaireID);
        const indexRef = firebase.collection('questionnaireIndex').doc(questionnaireID);
        
        const restoreTimestamp = new Date().toISOString();
        const restoredBy = getCurrentUserId();
        
        batch.update(questionnaireRef, {
            disabled: false,
            restoredAt: restoreTimestamp,
            restoredBy: restoredBy,
            deletedAt: firebase.FieldValue.delete(),
            deletedBy: firebase.FieldValue.delete(),
            deleteReason: firebase.FieldValue.delete()
        });
        
        batch.update(indexRef, {
            disabled: false,
            restoredAt: restoreTimestamp,
            restoredBy: restoredBy,
            status: 'restored'
        });
        
        await batch.commit();
        
        // Create audit log
        await createAuditLogEntry({
            action: 'questionnaire_restored',
            resourceId: questionnaireID,
            timestamp: restoreTimestamp,
            userId: restoredBy
        });
        
        return { success: true };
        
    } catch (error) {
        return {
            success: false,
            error: error.message
        };
    }
};

Audit Trail System

1. Delete Operation Logging

Comprehensive audit logging for all delete operations:

interface DeleteAuditEntry {
    id: string;
    action: 'questionnaire_deleted' | 'questionnaire_restored' | 'permanent_deletion';
    timestamp: string;
    userId: string;
    resourceId: string;
    resourceType: 'questionnaire';
    metadata: {
        questionnaireName: string;
        questionCount: number;
        hasSubmissions: boolean;
        linkedSites: string[];
        deleteReason: string;
        cascadeDeletes?: string[];
    };
    userAgent: string;
    ipAddress: string;
}
 
const createDeleteAuditEntry = async (
    operation: DeleteOperation
): Promise<void> => {
    const auditEntry: DeleteAuditEntry = {
        id: generateAuditId(),
        action: operation.type,
        timestamp: new Date().toISOString(),
        userId: getCurrentUserId(),
        resourceId: operation.questionnaireId,
        resourceType: 'questionnaire',
        metadata: {
            questionnaireName: operation.questionnaireName,
            questionCount: operation.questionCount,
            hasSubmissions: operation.hasSubmissions,
            linkedSites: operation.linkedSites,
            deleteReason: operation.reason,
            cascadeDeletes: operation.cascadeDeletes
        },
        userAgent: navigator.userAgent,
        ipAddress: await getUserIPAddress()
    };
    
    await firebase.collection('auditLogs').add(auditEntry);
};

2. Compliance and Retention

Data retention policies for regulatory compliance:

interface RetentionPolicy {
    deletedItemRetentionDays: number;
    auditLogRetentionDays: number;
    permanentDeletionWarningDays: number;
    complianceRequirements: ComplianceRequirement[];
}
 
const enforceRetentionPolicy = async (): Promise<void> => {
    const policy = await getOrganizationRetentionPolicy();
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - policy.deletedItemRetentionDays);
    
    // Find items eligible for permanent deletion
    const eligibleItems = await firebase
        .collection('questionnaires')
        .where('disabled', '==', true)
        .where('deletedAt', '<', cutoffDate.toISOString())
        .get();
    
    // Send warning notifications before permanent deletion
    const warningDate = new Date();
    warningDate.setDate(warningDate.getDate() - policy.permanentDeletionWarningDays);
    
    for (const doc of eligibleItems.docs) {
        const data = doc.data();
        if (new Date(data.deletedAt) < warningDate) {
            await sendPermanentDeletionWarning(doc.id, data);
        } else {
            await executePermanentDeletion(doc.id);
        }
    }
};

Download Selected Functionality

The download selected functionality provides comprehensive bulk export capabilities for questionnaires, supporting multiple formats and advanced filtering options. This system is designed for efficient data export with progress tracking and error handling.

Download Operation Architecture

flowchart TD
    A[User Selects Questionnaires] --> B[Download Modal]
    B --> C[Format Selection]
    C --> D[Export Options]
    D --> E[Validation Process]
    
    E --> F{Valid Selection?}
    F -->|No| G[Show Validation Errors]
    F -->|Yes| H[Prepare Export Request]
    
    H --> I[Send to Bulk Service]
    I --> J[Server Processing]
    J --> K[File Generation]
    K --> L[Progress Updates]
    
    L --> M{Generation Complete?}
    M -->|No| L
    M -->|Yes| N[Download Trigger]
    
    N --> O[Browser Download]
    O --> P[Success Notification]
    P --> Q[Reset Selection]
    
    subgraph "Error Handling"
        R[Network Errors]
        S[Server Errors]
        T[Format Errors]
        U[Permission Errors]
    end
    
    I --> R
    J --> S
    K --> T
    E --> U

Download Service Implementation

Primary Download Handler (QuestionnaireManager.js:141-162):

handleDownloadQuestionnaires = async () => {
    this.setState({ isLoading: true });
    
    const { questionnaireDownloadList } = this.state;
    const questionnaireIds = Object.keys(questionnaireDownloadList);
    
    try {
        const result = await downloadSelectedQuestionnaires(questionnaireIds);
        if (result.success) {
            toast.success('Questionnaires downloaded successfully');
        } else {
            toast.error(result.error || 'Failed to download questionnaires');
        }
    } catch (error) {
        console.error('Download failed:', error);
        toast.error('Failed to download questionnaires');
    } finally {
        this.setState({ isLoading: false });
        this.handleVisibleDownloadModal(false);
        this.handleResetDownloadList();
    }
};

Advanced Download Service

Bulk Download Service (bulkDownloadQuestionnaire.service.ts):

export const downloadSelectedQuestionnaires = async (
    questionnaireIds: string[]
): Promise<DownloadResult> => {
    try {
        // Validate inputs
        if (!questionnaireIds || questionnaireIds.length === 0) {
            throw new Error('No questionnaires selected for download');
        }
        
        if (questionnaireIds.length > MAX_DOWNLOAD_BATCH_SIZE) {
            throw new Error(`Cannot download more than ${MAX_DOWNLOAD_BATCH_SIZE} questionnaires at once`);
        }
        
        // Get authentication token
        const token = await getCurrentAuthToken();
        if (!token) {
            throw new Error('Authentication required for download');
        }
        
        // Prepare download request
        const downloadRequest: BulkDownloadRequest = {
            questionnaireIds,
            format: 'excel',
            includeMetadata: true,
            includeQuestions: true,
            includeResponses: false,
            compression: 'zip',
            timestamp: new Date().toISOString(),
            requestId: generateRequestId()
        };
        
        // Execute download request
        const response = await fetch(`${bulkOpsUrl}/bulk-edit/download`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`,
                'X-Request-ID': downloadRequest.requestId
            },
            body: JSON.stringify(downloadRequest)
        });
        
        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
        }
        
        // Handle successful response
        const contentType = response.headers.get('content-type');
        if (contentType?.includes('application/json')) {
            // Async download - return download URL
            const result = await response.json();
            return {
                success: true,
                downloadUrl: result.downloadUrl,
                expiresAt: result.expiresAt,
                fileSize: result.fileSize
            };
        } else {
            // Direct download - trigger browser download
            const blob = await response.blob();
            const downloadUrl = window.URL.createObjectURL(blob);
            
            const link = document.createElement('a');
            link.href = downloadUrl;
            link.download = generateDownloadFilename(questionnaireIds);
            link.style.display = 'none';
            
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            
            // Cleanup object URL
            setTimeout(() => window.URL.revokeObjectURL(downloadUrl), 1000);
            
            return { success: true };
        }
        
    } catch (error) {
        console.error('Download failed:', error);
        return {
            success: false,
            error: error.message
        };
    }
};

Download Format Support

1. Excel Format (.xlsx)

Comprehensive Excel export with multiple sheets and formatting:

interface ExcelExportStructure {
    sheets: {
        summary: SummarySheet;
        questionnaires: QuestionnaireSheet;
        questions: QuestionSheet;
        metadata: MetadataSheet;
    };
    formatting: ExcelFormatting;
    charts: ChartDefinition[];
}
 
const generateExcelExport = async (
    questionnaires: Questionnaire[]
): Promise<ExcelWorkbook> => {
    const workbook = new ExcelJS.Workbook();
    
    // Summary sheet
    const summarySheet = workbook.addWorksheet('Summary');
    summarySheet.columns = [
        { header: 'Metric', key: 'metric', width: 20 },
        { header: 'Value', key: 'value', width: 15 },
        { header: 'Description', key: 'description', width: 40 }
    ];
    
    summarySheet.addRows([
        { metric: 'Total Questionnaires', value: questionnaires.length, description: 'Number of questionnaires in export' },
        { metric: 'Export Date', value: new Date().toISOString(), description: 'When this export was generated' },
        { metric: 'Total Questions', value: calculateTotalQuestions(questionnaires), description: 'Sum of all questions across questionnaires' },
        { metric: 'Average Questions', value: calculateAverageQuestions(questionnaires), description: 'Average questions per questionnaire' }
    ]);
    
    // Questionnaires sheet
    const questionnairesSheet = workbook.addWorksheet('Questionnaires');
    questionnairesSheet.columns = [
        { header: 'ID', key: 'id', width: 30 },
        { header: 'Title', key: 'title', width: 40 },
        { header: 'Status', key: 'status', width: 15 },
        { header: 'Created Date', key: 'createdDate', width: 20 },
        { header: 'Updated Date', key: 'updatedDate', width: 20 },
        { header: 'Question Count', key: 'questionCount', width: 15 },
        { header: 'Tags', key: 'tags', width: 30 }
    ];
    
    questionnaires.forEach(q => {
        questionnairesSheet.addRow({
            id: q.id,
            title: q.title,
            status: q.status,
            createdDate: q.dateCreated,
            updatedDate: q.dateUpdated,
            questionCount: q.questions?.length || 0,
            tags: q.tags?.map(t => t.label).join(', ') || ''
        });
    });
    
    // Questions sheet
    const questionsSheet = workbook.addWorksheet('Questions');
    questionsSheet.columns = [
        { header: 'Questionnaire ID', key: 'questionnaireId', width: 30 },
        { header: 'Question ID', key: 'questionId', width: 30 },
        { header: 'Question Text', key: 'questionText', width: 50 },
        { header: 'Question Type', key: 'questionType', width: 20 },
        { header: 'Required', key: 'required', width: 10 },
        { header: 'Order', key: 'order', width: 10 }
    ];
    
    questionnaires.forEach(questionnaire => {
        questionnaire.questions?.forEach((question, index) => {
            questionsSheet.addRow({
                questionnaireId: questionnaire.id,
                questionId: question.id,
                questionText: question.text,
                questionType: question.type,
                required: question.required ? 'Yes' : 'No',
                order: index + 1
            });
        });
    });
    
    // Apply formatting
    applyExcelFormatting(workbook);
    
    return workbook;
};

2. JSON Format

Structured JSON export with complete data fidelity:

interface JSONExportStructure {
    metadata: ExportMetadata;
    questionnaires: QuestionnaireExport[];
    summary: ExportSummary;
}
 
const generateJSONExport = (questionnaires: Questionnaire[]): JSONExportStructure => {
    return {
        metadata: {
            exportDate: new Date().toISOString(),
            exportVersion: '2.0',
            exportedBy: getCurrentUserId(),
            totalQuestionnaires: questionnaires.length,
            exportFormat: 'json',
            schemaVersion: 'v1.2.0'
        },
        questionnaires: questionnaires.map(q => ({
            ...q,
            exportTimestamp: new Date().toISOString(),
            originalId: q.id,
            questions: q.questions?.map(question => ({
                ...question,
                exportOrder: question.order || 0,
                hasConditionalLogic: !!question.conditionalLogic,
                hasValidation: !!question.validationRules
            }))
        })),
        summary: {
            totalQuestions: questionnaires.reduce((total, q) => total + (q.questions?.length || 0), 0),
            questionTypes: getQuestionTypeDistribution(questionnaires),
            statusDistribution: getStatusDistribution(questionnaires),
            tagDistribution: getTagDistribution(questionnaires)
        }
    };
};

Progressive Download System

1. Large Dataset Handling

For large questionnaire datasets, implement progressive downloading:

const downloadLargeDataset = async (
    questionnaireIds: string[],
    onProgress: (progress: ProgressInfo) => void
): Promise<void> => {
    const CHUNK_SIZE = 10;
    const chunks = chunkArray(questionnaireIds, CHUNK_SIZE);
    const results: DownloadChunk[] = [];
    
    for (let i = 0; i < chunks.length; i++) {
        const chunk = chunks[i];
        
        onProgress({
            stage: 'downloading',
            current: i + 1,
            total: chunks.length,
            message: `Processing chunk ${i + 1} of ${chunks.length}`
        });
        
        try {
            const chunkResult = await downloadQuestionnaireChunk(chunk);
            results.push(chunkResult);
        } catch (error) {
            console.error(`Chunk ${i + 1} failed:`, error);
            onProgress({
                stage: 'error',
                current: i + 1,
                total: chunks.length,
                message: `Chunk ${i + 1} failed: ${error.message}`,
                error: error
            });
            throw error;
        }
    }
    
    // Combine chunks
    onProgress({
        stage: 'combining',
        current: chunks.length,
        total: chunks.length,
        message: 'Combining downloaded data'
    });
    
    const combinedData = combineDownloadChunks(results);
    
    // Generate final file
    onProgress({
        stage: 'generating',
        current: chunks.length,
        total: chunks.length,
        message: 'Generating download file'
    });
    
    await generateDownloadFile(combinedData);
    
    onProgress({
        stage: 'complete',
        current: chunks.length,
        total: chunks.length,
        message: 'Download complete'
    });
};

2. Background Download Processing

For very large exports, use background processing with status polling:

const initiateBackgroundDownload = async (
    questionnaireIds: string[]
): Promise<BackgroundDownloadResponse> => {
    const response = await fetch(`${apiUrl}/questionnaires/background-download`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${await getCurrentAuthToken()}`
        },
        body: JSON.stringify({
            questionnaireIds,
            format: 'excel',
            notificationEmail: getUserEmail()
        })
    });
    
    if (!response.ok) {
        throw new Error(`Background download failed: ${response.statusText}`);
    }
    
    return await response.json();
};
 
const pollDownloadStatus = async (
    downloadId: string,
    onStatusUpdate: (status: DownloadStatus) => void
): Promise<void> => {
    const pollInterval = 2000; // 2 seconds
    const maxAttempts = 300; // 10 minutes max
    let attempts = 0;
    
    const poll = async (): Promise<void> => {
        try {
            const response = await fetch(`${apiUrl}/questionnaires/download-status/${downloadId}`, {
                headers: {
                    'Authorization': `Bearer ${await getCurrentAuthToken()}`
                }
            });
            
            if (!response.ok) {
                throw new Error(`Status poll failed: ${response.statusText}`);
            }
            
            const status: DownloadStatus = await response.json();
            onStatusUpdate(status);
            
            if (status.status === 'completed') {
                return; // Done
            } else if (status.status === 'failed') {
                throw new Error(status.error || 'Download failed');
            } else if (attempts >= maxAttempts) {
                throw new Error('Download timeout');
            }
            
            // Continue polling
            attempts++;
            setTimeout(poll, pollInterval);
            
        } catch (error) {
            onStatusUpdate({
                status: 'failed',
                error: error.message,
                downloadId
            });
        }
    };
    
    await poll();
};

Download Progress Tracking

Real-time Progress Updates:

interface DownloadProgress {
    stage: 'preparing' | 'downloading' | 'processing' | 'generating' | 'complete' | 'error';
    percentage: number;
    currentItem: number;
    totalItems: number;
    message: string;
    estimatedTimeRemaining?: number;
    downloadSpeed?: number;
    error?: Error;
}
 
const trackDownloadProgress = (
    downloadId: string,
    onProgress: (progress: DownloadProgress) => void
): (() => void) => {
    let cancelled = false;
    
    const updateProgress = async () => {
        if (cancelled) return;
        
        try {
            const response = await fetch(`${apiUrl}/download-progress/${downloadId}`);
            const progress: DownloadProgress = await response.json();
            
            onProgress(progress);
            
            if (progress.stage === 'complete' || progress.stage === 'error') {
                return; // Stop tracking
            }
            
            // Continue tracking
            setTimeout(updateProgress, 1000);
            
        } catch (error) {
            if (!cancelled) {
                onProgress({
                    stage: 'error',
                    percentage: 0,
                    currentItem: 0,
                    totalItems: 0,
                    message: 'Progress tracking failed',
                    error: error
                });
            }
        }
    };
    
    updateProgress();
    
    // Return cancellation function
    return () => {
        cancelled = true;
    };
};

Download Security and Validation

1. Permission Validation

Comprehensive permission checking before download:

const validateDownloadPermissions = async (
    questionnaireIds: string[]
): Promise<PermissionValidationResult> => {
    const userPermissions = await getCurrentUserPermissions();
    const organizationSettings = await getOrganizationSettings();
    
    const validationResult: PermissionValidationResult = {
        canDownload: true,
        restrictedQuestionnaires: [],
        warnings: [],
        errors: []
    };
    
    // Check global download permission
    if (!userPermissions.canDownloadQuestionnaires) {
        validationResult.canDownload = false;
        validationResult.errors.push('User does not have download permissions');
        return validationResult;
    }
    
    // Check organization limits
    if (questionnaireIds.length > organizationSettings.maxDownloadBatchSize) {
        validationResult.canDownload = false;
        validationResult.errors.push(`Cannot download more than ${organizationSettings.maxDownloadBatchSize} questionnaires`);
        return validationResult;
    }
    
    // Check individual questionnaire permissions
    for (const id of questionnaireIds) {
        const questionnaire = await getQuestionnaire(id);
        if (!questionnaire) {
            validationResult.restrictedQuestionnaires.push(id);
            validationResult.warnings.push(`Questionnaire ${id} not found`);
            continue;
        }
        
        if (!canUserAccessQuestionnaire(userPermissions, questionnaire)) {
            validationResult.restrictedQuestionnaires.push(id);
            validationResult.warnings.push(`No access to questionnaire: ${questionnaire.title}`);
        }
        
        if (questionnaire.confidentialityLevel === 'restricted' && !userPermissions.canAccessRestricted) {
            validationResult.restrictedQuestionnaires.push(id);
            validationResult.warnings.push(`Restricted access questionnaire: ${questionnaire.title}`);
        }
    }
    
    // Filter out restricted questionnaires if partial download allowed
    if (organizationSettings.allowPartialDownloads && validationResult.restrictedQuestionnaires.length > 0) {
        validationResult.warnings.push(`${validationResult.restrictedQuestionnaires.length} questionnaires excluded due to permissions`);
    } else if (validationResult.restrictedQuestionnaires.length > 0) {
        validationResult.canDownload = false;
        validationResult.errors.push('Cannot download due to permission restrictions');
    }
    
    return validationResult;
};

2. Data Sanitization

Ensure exported data complies with privacy and security requirements:

const sanitizeQuestionnaireForExport = (
    questionnaire: Questionnaire,
    sanitizationLevel: 'none' | 'basic' | 'strict'
): Questionnaire => {
    if (sanitizationLevel === 'none') {
        return questionnaire;
    }
    
    const sanitized = { ...questionnaire };
    
    if (sanitizationLevel === 'basic' || sanitizationLevel === 'strict') {
        // Remove sensitive metadata
        delete sanitized.modifiedBy;
        delete sanitized.createdBy;
        delete sanitized.auditLog;
        delete sanitized.internalNotes;
        
        // Sanitize questions
        sanitized.questions = sanitized.questions?.map(question => {
            const sanitizedQuestion = { ...question };
            delete sanitizedQuestion.internalId;
            delete sanitizedQuestion.debugInfo;
            
            if (sanitizationLevel === 'strict') {
                delete sanitizedQuestion.conditionalLogic;
                delete sanitizedQuestion.validationRules;
                delete sanitizedQuestion.metadata;
            }
            
            return sanitizedQuestion;
        });
    }
    
    if (sanitizationLevel === 'strict') {
        // Remove all internal identifiers
        delete sanitized.questionnaireIndexID;
        delete sanitized.organizationId;
        delete sanitized.tags;
        delete sanitized.autoAssignment;
    }
    
    return sanitized;
};

Download Analytics and Monitoring

Usage Tracking:

const trackDownloadUsage = async (
    downloadRequest: DownloadRequest,
    downloadResult: DownloadResult
): Promise<void> => {
    const analyticsData = {
        event: 'questionnaire_bulk_download',
        userId: getCurrentUserId(),
        timestamp: new Date().toISOString(),
        properties: {
            questionnaireCount: downloadRequest.questionnaireIds.length,
            format: downloadRequest.format,
            success: downloadResult.success,
            fileSize: downloadResult.fileSize,
            processingTime: downloadResult.processingTime,
            downloadMethod: downloadResult.downloadUrl ? 'async' : 'direct',
            userAgent: navigator.userAgent,
            organizationId: getOrganizationId()
        }
    };
    
    // Track with analytics service
    await sendAnalyticsEvent(analyticsData);
    
    // Track with internal monitoring
    await logDownloadEvent({
        type: 'bulk_download',
        status: downloadResult.success ? 'success' : 'failure',
        resourceCount: downloadRequest.questionnaireIds.length,
        format: downloadRequest.format,
        userId: getCurrentUserId(),
        organizationId: getOrganizationId(),
        timestamp: new Date().toISOString(),
        metadata: {
            fileSize: downloadResult.fileSize,
            processingTime: downloadResult.processingTime,
            error: downloadResult.error
        }
    });
};

Data Flow Diagrams

Overall System Data Flow

graph TB
    subgraph "Client Layer"
        A[React Components]
        B[Redux Store]
        C[Redux Saga]
        D[Local Storage]
    end
    
    subgraph "API Layer"
        E[REST API Gateway]
        F[Authentication Service]
        G[Bulk Operations Service]
        H[File Generation Service]
    end
    
    subgraph "Data Layer"
        I[Firebase Realtime DB]
        J[Cloud Firestore]
        K[File Storage]
        L[Cache Layer]
    end
    
    subgraph "External Services"
        M[Google Analytics]
        N[Sentry Monitoring]
        O[AI/ML Services]
    end
    
    A -->|Actions| B
    B -->|Triggers| C
    C -->|API Calls| E
    E -->|Auth Check| F
    E -->|Data Queries| I
    E -->|Bulk Ops| G
    G -->|File Gen| H
    H -->|Store Files| K
    
    I -->|Real-time Updates| B
    J -->|Document Queries| E
    
    A -->|Analytics| M
    C -->|Error Tracking| N
    E -->|AI Requests| O
    
    B -->|Persistence| D
    L -->|Cache Hits| E

Questionnaire List Data Flow

sequenceDiagram
    participant U as User
    participant QM as QuestionnaireManager
    participant RS as Redux Store
    participant S as Saga
    participant API as API Service
    participant FB as Firebase
    
    U->>QM: Navigate to /admin/questionnaires
    QM->>RS: componentDidMount()
    RS->>S: fetchPaginateQuestionnairesAsync
    S->>API: GET /questionnaires/paginate
    API->>FB: Query questionnaires collection
    FB-->>API: Return paginated data
    API-->>S: Questionnaire list
    S->>RS: UPDATE questionnaires state
    RS-->>QM: Re-render with data
    
    U->>QM: Sort by title
    QM->>RS: setSortQuestionnaire('title', 'asc')
    RS->>S: Trigger componentDidUpdate
    S->>API: GET /questionnaires/paginate?sort=title&dir=asc
    API->>FB: Query with sorting
    FB-->>API: Sorted data
    API-->>S: Sorted questionnaire list
    S->>RS: UPDATE questionnaires state
    RS-->>QM: Re-render with sorted data
    
    U->>QM: Select questionnaires for download
    QM->>QM: handleClickCheckbox()
    QM->>QM: Update local selection state
    
    U->>QM: Click download button
    QM->>S: downloadSelectedQuestionnaires()
    S->>API: POST /bulk-edit/download
    API->>API: Generate export file
    API-->>S: Download URL or file stream
    S->>QM: Trigger browser download
    QM-->>U: File downloaded

Clone Operation Data Flow

flowchart TD
    A[User clicks Clone] --> B[QuestionnaireListModal opens]
    B --> C[User confirms clone]
    C --> D[cloneQuestionnaireAsync.request]
    
    D --> E[Saga: cloneQuestionnaireSaga]
    E --> F[Validate permissions]
    F --> G{Has permission?}
    
    G -->|No| H[Show error message]
    G -->|Yes| I[Check title conflicts]
    
    I --> J{Title exists?}
    J -->|Yes| K[Generate unique title]
    J -->|No| L[Use original title]
    
    K --> M[Prepare clone data]
    L --> M
    
    M --> N[Create questionnaire index]
    N --> O[Create questionnaire content]
    O --> P[Update relationships]
    P --> Q[Refresh UI data]
    Q --> R[Show success message]
    
    H --> S[User retry or cancel]
    R --> T[Operation complete]

Delete Operation Data Flow

stateDiagram-v2
    [*] --> DeleteRequest
    DeleteRequest --> PermissionCheck
    
    PermissionCheck --> AccessDenied : No permission
    PermissionCheck --> DependencyCheck : Has permission
    
    DependencyCheck --> DependencyWarning : Has dependencies
    DependencyCheck --> SoftDelete : No dependencies
    
    DependencyWarning --> UserDecision
    UserDecision --> CancelDelete : User cancels
    UserDecision --> SoftDelete : User confirms
    
    SoftDelete --> UpdateQuestionnaire
    UpdateQuestionnaire --> UpdateIndex
    UpdateIndex --> UpdateRelated
    UpdateRelated --> CreateAuditLog
    CreateAuditLog --> RefreshUI
    RefreshUI --> ShowSuccess
    ShowSuccess --> [*]
    
    AccessDenied --> [*]
    CancelDelete --> [*]
    
    UpdateQuestionnaire --> ErrorState : Database error
    UpdateIndex --> ErrorState : Database error
    ErrorState --> ShowError
    ShowError --> [*]

Bulk Upload Data Flow

graph TD
    A[User selects Excel file] --> B[File validation]
    B --> C{Valid file?}
    C -->|No| D[Show error message]
    C -->|Yes| E[Parse Excel content]
    
    E --> F[Validate questionnaire data]
    F --> G{Valid data?}
    G -->|No| H[Show validation errors]
    G -->|Yes| I[Process questionnaires]
    
    I --> J[Create questionnaire entries]
    J --> K[Create index entries]
    K --> L[Associate with departments]
    L --> M[Update related entities]
    M --> N[Refresh UI data]
    N --> O[Show success message]
    
    D --> P[User retry]
    H --> P
    P --> A
    O --> Q[Operation complete]

Implementation Details

State Management Implementation

The questionnaire module uses a sophisticated Redux architecture with multiple specialized reducers and middleware for handling complex state interactions.

Redux Store Configuration

// store/rootReducers.ts
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import { firebaseReducer } from 'react-redux-firebase';
import { firestoreReducer } from 'redux-firestore';
 
import questionnaireReducer from '../reducers/questionnaire/questionnaire.reducer';
import questionnaireByPropsReducer from '../reducers/questionnaireByProps.reducer';
import questionnaireIndexReducer from '../reducers/questionnaireIndex/questionnaireIndex.reducer';
import questionnaireDetailReducer from '../reducers/questionnaireDetail/questionnaireDetail.reducer';
 
const createRootReducer = (history) => combineReducers({
    router: connectRouter(history),
    firebase: firebaseReducer,
    firestore: firestoreReducer,
    questionnaire: questionnaireReducer,
    questionnaireByProps: questionnaireByPropsReducer,
    questionnaireIndex: questionnaireIndexReducer,
    questionnaireDetail: questionnaireDetailReducer,
    // ... other reducers
});
 
export type RootState = ReturnType<ReturnType<typeof createRootReducer>>;

Middleware Configuration

// store/configStore.ts
import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { routerMiddleware } from 'connected-react-router';
import { createBrowserHistory } from 'history';
 
import createRootReducer from './rootReducers';
import rootSaga from '../sagas/rootSaga';
 
export const history = createBrowserHistory();
const sagaMiddleware = createSagaMiddleware();
 
const composeEnhancers = 
    (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || 
    compose;
 
const store = createStore(
    createRootReducer(history),
    composeEnhancers(
        applyMiddleware(
            routerMiddleware(history),
            sagaMiddleware
        )
    )
);
 
sagaMiddleware.run(rootSaga);
 
export { store };

Questionnaire Reducer Implementation

// reducers/questionnaire/questionnaire.reducer.ts
import { createReducer } from 'typesafe-actions';
import * as actions from './questionnaire.action';
import { QuestionnairesState } from './type';
 
const initialState: QuestionnairesState = {
    filterQuery: '',
    sortBy: 'title',
    page: 1,
    totalItem: 0,
    status: '',
    title: '',
    createdAt: '',
    dateCreated: '',
    dateUpdated: '',
    questions: '',
    paginateQuestionnaires: null,
    questionnaires: null,
    modalShown: null,
    modalBulkShown: false,
    selectedQuestionnaire: null,
    index: {},
    tab: 'published',
    isLoadingTable: false,
    isLoading: false,
};
 
const questionnaireReducer = createReducer(initialState)
    .handleAction(actions.setPage, (state, action) => ({
        ...state,
        page: action.payload.page
    }))
    .handleAction(actions.setSortQuestionnaire, (state, action) => ({
        ...state,
        sortBy: action.payload.sortBy,
        [action.payload.sortBy]: action.payload[action.payload.sortBy]
    }))
    .handleAction(actions.showQuestionnaireModal, (state, action) => ({
        ...state,
        modalShown: action.payload.modalShown,
        selectedQuestionnaire: action.payload.selectedQuestionnaire
    }))
    .handleAction(actions.fetchPaginateQuestionnairesAsync.request, (state) => ({
        ...state,
        isLoadingTable: true
    }))
    .handleAction(actions.fetchPaginateQuestionnairesAsync.success, (state, action) => ({
        ...state,
        paginateQuestionnaires: action.payload,
        isLoadingTable: false,
        totalItem: action.payload.totalItem
    }))
    .handleAction(actions.fetchPaginateQuestionnairesAsync.failure, (state) => ({
        ...state,
        isLoadingTable: false
    }));
 
export default questionnaireReducer;

Component Lifecycle Management

QuestionnaireManager Lifecycle

class QuestionnaireManager extends React.Component {
    componentDidMount() {
        // Initialize data loading
        this.props.fetchPaginateQuestionnairesAsync({ 
            sortFields: 'title', 
            sortDirections: 'asc' 
        });
        this.props.getQuestionnairesByPropsAsync();
        
        // Set up real-time listeners
        this.setupRealtimeListeners();
        
        // Initialize analytics tracking
        this.trackPageView();
    }
    
    componentDidUpdate(prevProps) {
        const { 
            page, filterQuery, status, title, createdAt, 
            dateCreated, dateUpdated, questions, sortBy 
        } = this.props.questionnaireFilter;
        
        const { questionnaireFilter, activeTab } = prevProps;
        
        // Handle tab changes
        if (activeTab !== this.props.activeTab) {
            this.handleTabChange();
            return;
        }
        
        // Handle filter changes
        if (this.hasFilterChanged(prevProps.questionnaireFilter, this.props.questionnaireFilter)) {
            this.handleFilterChange();
        }
        
        // Handle sort changes
        if (prevProps.questionnaireSortBy !== this.props.questionnaireSortBy) {
            this.handleSortChange();
        }
    }
    
    componentWillUnmount() {
        // Cleanup real-time listeners
        this.cleanupRealtimeListeners();
        
        // Cancel pending API requests
        this.cancelPendingRequests();
        
        // Save current state to session storage
        this.saveSessionState();
    }
    
    private setupRealtimeListeners() {
        // Firebase real-time listeners for live updates
        const { organization } = this.props.profile;
        const questionnaireRef = firebase.database().ref(`questionnaires/${organization}`);
        
        this.questionnaireListener = questionnaireRef.on('value', (snapshot) => {
            this.handleRealtimeUpdate(snapshot.val());
        });
    }
    
    private hasFilterChanged(prev, current) {
        return (
            prev.sortBy !== current.sortBy ||
            prev.page !== current.page ||
            prev.filterQuery !== current.filterQuery ||
            prev.status !== current.status ||
            prev.title !== current.title ||
            prev.createdAt !== current.createdAt ||
            prev.dateCreated !== current.dateCreated ||
            prev.dateUpdated !== current.dateUpdated ||
            prev.questions !== current.questions
        );
    }
}

Error Handling Implementation

Global Error Boundary

// components/global/ErrorBoundary.tsx
import React, { Component, ErrorInfo, ReactNode } from 'react';
import * as Sentry from '@sentry/react';
 
interface Props {
    children: ReactNode;
    fallback?: ReactNode;
}
 
interface State {
    hasError: boolean;
    error?: Error;
    errorInfo?: ErrorInfo;
}
 
class ErrorBoundary extends Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = { hasError: false };
    }
    
    static getDerivedStateFromError(error: Error): State {
        return {
            hasError: true,
            error
        };
    }
    
    componentDidCatch(error: Error, errorInfo: ErrorInfo) {
        console.error('ErrorBoundary caught an error:', error, errorInfo);
        
        // Log to Sentry
        Sentry.withScope((scope) => {
            scope.setContext('errorBoundary', {
                componentStack: errorInfo.componentStack,
                errorBoundary: true
            });
            Sentry.captureException(error);
        });
        
        this.setState({
            error,
            errorInfo
        });
    }
    
    render() {
        if (this.state.hasError) {
            return this.props.fallback || (
                <div className="error-boundary">
                    <h2>Something went wrong</h2>
                    <details style={{ whiteSpace: 'pre-wrap' }}>
                        {this.state.error && this.state.error.toString()}
                        <br />
                        {this.state.errorInfo.componentStack}
                    </details>
                </div>
            );
        }
        
        return this.props.children;
    }
}
 
export default ErrorBoundary;

API Error Handling

// utils/apiErrorHandler.ts
import { toast } from 'react-toastify';
import * as Sentry from '@sentry/react';
 
export interface APIError {
    message: string;
    status: number;
    code?: string;
    details?: any;
}
 
export const handleAPIError = (error: any, context: string = 'API'): APIError => {
    let apiError: APIError;
    
    if (error.response) {
        // HTTP error response
        apiError = {
            message: error.response.data?.message || error.response.statusText,
            status: error.response.status,
            code: error.response.data?.code,
            details: error.response.data?.details
        };
    } else if (error.request) {
        // Network error
        apiError = {
            message: 'Network error. Please check your connection.',
            status: 0,
            code: 'NETWORK_ERROR'
        };
    } else {
        // Other error
        apiError = {
            message: error.message || 'An unexpected error occurred',
            status: -1,
            code: 'UNKNOWN_ERROR'
        };
    }
    
    // Log to console for development
    console.error(`${context} Error:`, apiError);
    
    // Log to Sentry for production monitoring
    Sentry.withScope((scope) => {
        scope.setTag('errorType', 'api');
        scope.setContext('apiError', {
            context,
            status: apiError.status,
            code: apiError.code,
            message: apiError.message
        });
        Sentry.captureException(new Error(`${context}: ${apiError.message}`));
    });
    
    // Show user-friendly message
    const userMessage = getUserFriendlyMessage(apiError);
    toast.error(userMessage);
    
    return apiError;
};
 
const getUserFriendlyMessage = (error: APIError): string => {
    switch (error.status) {
        case 401:
            return 'Please log in to continue';
        case 403:
            return 'You do not have permission to perform this action';
        case 404:
            return 'The requested resource was not found';
        case 429:
            return 'Too many requests. Please try again later';
        case 500:
            return 'Server error. Please try again later';
        case 0:
            return 'Network connection error. Please check your internet connection';
        default:
            return error.message || 'An error occurred. Please try again';
    }
};

File Structure

Complete Directory Structure

src/
├── components/
│   ├── questionnaires/
│   │   ├── QuestionnaireManager.js                     # Main orchestration component
│   │   ├── QuestionnaireList.tsx                       # Data table component
│   │   ├── QuestionnaireListHeader.tsx                 # Header with actions and filters
│   │   ├── QuestionnaireListModal.tsx                  # Clone/delete confirmation modal
│   │   ├── QuestionnaireBulkModal.tsx                  # Bulk upload interface
│   │   ├── QuestionnaireBulkModalContainer.tsx         # Bulk modal container
│   │   ├── QuestionnaireDownloadModal.tsx              # Download confirmation modal
│   │   ├── QuestionnaireDeleteModal.tsx                # Delete confirmation modal
│   │   ├── QuestionnaireHistoryList.tsx                # Deleted items list
│   │   ├── QuestionnaireEditor/                        # Questionnaire editing components
│   │   │   ├── QuestionnaireEditor.tsx                 # Main editor component (1038 lines)
│   │   │   ├── QuestionnaireEditorContainer.tsx        # Editor container
│   │   │   ├── QuestionnaireQuestions/                 # Question type components
│   │   │   │   ├── QuestionnaireQuestionsMultipleChoice.tsx
│   │   │   │   ├── QuestionnaireQuestionsScale.tsx
│   │   │   │   ├── QuestionnaireQuestionsRangeFlag.tsx
│   │   │   │   ├── QuestionnaireQuestionsLabelFlag.tsx
│   │   │   │   ├── QuestionnaireQuestionsInventory.tsx
│   │   │   │   ├── QuestionnaireQuestionsBinary.tsx
│   │   │   │   ├── QuestionnaireQuestionsText.tsx
│   │   │   │   ├── QuestionnaireQuestionsChecklist.tsx
│   │   │   │   └── QuestionnaireQuestions.styles.ts    # Styled components
│   │   │   ├── QuestionnaireConditional/               # Conditional logic components
│   │   │   │   ├── QuestionnaireConditionalLogic.tsx
│   │   │   │   ├── QuestionnaireConditionalBuilder.tsx
│   │   │   │   └── QuestionnaireConditionalTypes.ts
│   │   │   └── utils/                                  # Editor utilities
│   │   │       ├── fetchQuestionnaireByID.ts
│   │   │       ├── updateQuestionnaire.ts
│   │   │       ├── getQuestionTypeOptions.ts
│   │   │       └── updateStackSliderValue.ts
│   │   └── utils/                                      # General questionnaire utilities
│   │       ├── deleteQuestionnaire.ts                  # Deletion logic with validation
│   │       ├── downloadQuestionnaires.ts               # Export functionality
│   │       ├── uploadBulkQuestionnaire.ts              # Excel import processing
│   │       ├── getQuestionnaireIndex.ts                # Index management
│   │       └── renderQuestionnaireValidation.js        # Form validation
├── pages/
│   ├── questionnaires.js                               # Main questionnaire page
│   └── questionnaires-edit.js                         # Questionnaire editor page
├── routes/
│   ├── admin-routes.js                                 # Admin route definitions
│   ├── superadmin-routes.js                           # Super admin routes
│   └── LmsRoutes.tsx                                   # LMS-specific routes
├── reducers/
│   ├── questionnaire/                                  # Main questionnaire state
│   │   ├── questionnaire.action.ts                    # 113 lines of actions
│   │   ├── questionnaire.actionTypes.ts               # Action type constants
│   │   ├── questionnaire.reducer.ts                   # State reducer logic
│   │   └── type.d.ts                                   # TypeScript definitions
│   ├── questionnaireByProps/                           # Optimized data fetching
│   │   ├── questionnaireByProps.action.ts
│   │   ├── questionnaireByProps.reducer.ts
│   │   └── type.d.ts
│   ├── questionnaireIndex/                             # Index management
│   │   ├── questionnaireIndex.action.ts
│   │   ├── questionnaireIndex.reducer.ts
│   │   └── type.d.ts
│   └── questionnaireDetail/                            # Detailed editing state
│       ├── questionnaireDetail.action.ts
│       ├── questionnaireDetail.reducer.ts
│       └── type.d.ts
├── sagas/
│   └── questionnaire/
│       ├── questionnaire.actionSaga.ts                 # Complex async operations (368 lines)
│       ├── questionnaireByProps.saga.ts               # Optimized data saga
│       └── questionnaireIndex.saga.ts                 # Index management saga
├── services/
│   ├── questionnaire.service.ts                       # Main API interface (125 lines)
│   ├── fetchQuestionnaireByProps.ts                   # Optimized data fetching
│   └── bulkOpsRevamp/
│       ├── bulkDownloadQuestionnaire.service.ts       # Bulk download service
│       ├── bulkUploadQuestionnaire.service.ts         # Bulk upload service
│       └── bulkOperationsTypes.ts                     # Type definitions
├── lang/                                               # Internationalization
│   ├── en/
│   │   └── pageQuestionnaire.json                     # English translations
│   ├── es/
│   │   └── pageQuestionnaire.json                     # Spanish translations
│   ├── id/
│   │   └── pageQuestionnaire.json                     # Indonesian translations
│   ├── pt/
│   │   └── pageQuestionnaire.json                     # Portuguese translations
│   └── th/
│       └── pageQuestionnaire.json                     # Thai translations
├── assets/
│   ├── dummy/
│   │   └── json/
│   │       └── issuesQuestionnaireStat/               # Mock data for development
│   └── icon/
│       ├── sales-warning.svg                          # Warning icon for modals
│       ├── trash-dark.svg                             # Delete icon
│       └── green-check.svg                            # Success indicator
├── utils/
│   ├── reactselectstyles.ts                           # React Select styling
│   └── apiConfig.ts                                   # API configuration
├── styles/
│   ├── General.ts                                      # General styled components
│   └── Button.ts                                      # Button styled components
└── __tests__/
    └── components/
        └── questionnaires/
            ├── QuestionnaireBulkModal.test.tsx         # Unit tests
            ├── QuestionnaireListModal.test.tsx
            └── QuestionnaireManager.test.tsx

Key File Descriptions

Core Components

  1. QuestionnaireManager.js (366 lines)

    • Primary orchestration component
    • Manages state coordination between child components
    • Handles pagination, sorting, and filtering logic
    • Implements bulk selection and download functionality
  2. QuestionnaireListModal.tsx (210 lines)

    • Dual-purpose modal for clone and delete operations
    • Implements Firebase real-time database operations
    • Handles confirmation workflows with user feedback
  3. QuestionnaireBulkModal.tsx (808 lines)

    • Complex multi-step wizard for Excel uploads
    • File validation and error reporting
    • Department selection and template downloading

State Management

  1. questionnaire.action.ts (113 lines)

    • Complete action creators for questionnaire operations
    • Type-safe action definitions using typesafe-actions
    • Async action creators for saga integration
  2. questionnaire.actionSaga.ts (368 lines)

    • Complex async operation management
    • API coordination and error handling
    • Side effect management for questionnaire operations

Services and APIs

  1. questionnaire.service.ts (125 lines)

    • Main API service interface
    • REST endpoint definitions and implementations
    • Authentication and error handling
  2. bulkDownloadQuestionnaire.service.ts (200+ lines)

    • Specialized bulk download operations
    • File generation and streaming
    • Progress tracking and error handling

Utilities and Helpers

  1. deleteQuestionnaire.ts (150+ lines)

    • Comprehensive deletion logic
    • Dependency checking and validation
    • Soft delete implementation with audit trails
  2. uploadBulkQuestionnaire.ts (200+ lines)

    • Excel file parsing and validation
    • Bulk questionnaire creation logic
    • Error reporting and progress tracking

Configuration Files

Package Dependencies

  • package.json: 274 lines defining 158 dependencies and 84 dev dependencies
  • yarn.lock: Comprehensive dependency lock file ensuring reproducible builds

TypeScript Configuration

  • tsconfig.json: TypeScript compiler configuration
  • Type Definition Files: Comprehensive type safety across the module

Build and Development

  • config-overrides.js: Webpack configuration overrides
  • .env files: Environment-specific configuration

Testing Structure

Unit Tests

  • Component-level testing using React Testing Library
  • Redux action and reducer testing
  • Service layer testing with mocked API responses

Integration Tests

  • Full workflow testing for complex operations
  • Cross-component interaction testing
  • State management integration testing

End-to-End Tests

  • User journey testing with Cypress
  • Complete questionnaire management workflows
  • Performance and accessibility testing

This comprehensive technical documentation provides detailed insights into the questionnaire list module’s architecture, implementation, and functionality. The system demonstrates sophisticated software engineering practices with robust error handling, comprehensive state management, and scalable architecture patterns suitable for enterprise-level applications.