npm package: @phygitallabs/survey
Description: A comprehensive survey management system built with SurveyJS, React, TypeScript, and PostgreSQL, designed for enterprise-grade survey creation, administration, and analytics.
Latest version: 6.3.1

# Survey Package

A comprehensive survey management system built with SurveyJS, React, TypeScript, and PostgreSQL, designed for enterprise-grade survey creation, administration, and analytics.

## Features

- **🏗️ Full-Stack Architecture**: Complete solution with PostgreSQL database, tRPC API, and React components
- **🎨 Modern UI**: Built with SurveyJS and Ant Design v5 for professional, responsive interfaces
- **📊 Advanced Analytics**: Real-time survey analytics with comprehensive statistics
- **🔒 Security**: JWT-based authentication with admin role management
- **🏢 Multi-tenancy**: Project-based survey organization with flexible labeling
- **⚡ Type-Safe**: End-to-end TypeScript with Zod validation and Drizzle ORM
- **🔄 Real-time Updates**: Live response tracking and instant analytics generation
- **📱 Responsive Design**: Works seamlessly on desktop, tablet, and mobile devices
- **🎯 Survey Management**: Complete CRUD operations with pagination and filtering
- **📋 Import/Export**: JSON-based survey definitions with validation
- **🔍 Results Visualization**: Professional analytics dashboard with detailed insights

## Installation

```bash
# From the root of the monorepo
cd packages/survey
npm install
```

## Project Structure

```
packages/survey/
├── 📁 src/
│   ├── 📁 admin/                    # Admin UI components
│   │   └── 📁 components/
│   │       ├── SurveyCreator.tsx    # Survey creation/editing
│   │       ├── SurveyList.tsx       # Survey management interface
│   │       └── SurveyImport.tsx     # JSON import with validation
│   │
│   ├── 📁 api/                      # Client-side API layer
│   │   └── surveyApi.ts             # Survey API abstraction
│   │
│   ├── 📁 client/                   # Client-facing components
│   │   ├── 📁 components/
│   │   │   ├── SurveyRenderer.tsx   # Survey display and interaction
│   │   │   └── SurveyResults.tsx    # Analytics visualization
│   │   └── 📁 hooks/
│   │       └── useSurveySubmission.ts # Survey submission logic
│   │
│   ├── 📁 core/                     # Shared core functionality
│   │   ├── 📁 hooks/
│   │   │   └── useSurvey.ts         # Main survey management hook
│   │   ├── 📁 types/
│   │   │   └── survey.types.ts      # TypeScript interfaces
│   │   └── 📁 utils/
│   │       └── surveyHelpers.ts     # Utility functions and analytics
│   │
│   ├── 📁 db/                       # Database schema and types
│   │   ├── schema.ts                # Drizzle ORM schema definitions
│   │   └── index.ts                 # Database exports
│   │
│   ├── 📁 server/                   # Server-side tRPC implementation
│   │   ├── 📁 routers/
│   │   │   ├── survey.ts            # Survey CRUD operations
│   │   │   ├── response.ts          # Response management
│   │   │   └── analytics.ts         # Analytics generation
│   │   ├── 📁 middleware/
│   │   │   └── admin.ts             # Admin authentication
│   │   ├── context.ts               # tRPC context setup
│   │   ├── router.ts                # Main router configuration
│   │   └── trpc.ts                  # tRPC configuration
│   │
│   ├── 📁 scripts/                  # Database utilities
│   │   └── migrate.ts               # Database migration script
│   │
│   └── 📁 themes/                   # UI theming
│       └── default.ts               # Default SurveyJS theme
│
├── 📁 drizzle/                      # Database migrations
│   ├── 📁 meta/                     # Migration metadata
│   └── *.sql                        # Migration files
│
├── 📄 index.ts                      # Client-side exports
├── 📄 server.ts                     # Server-side exports
├── 📄 drizzle.config.ts             # Database configuration
├── 📄 migrate.ts                    # Migration runner
├── 📄 package.json                  # Dependencies and scripts
├── 📄 tsconfig.json                 # TypeScript configuration
├── 📄 tsup.config.ts                # Build configuration
├── 📄 README.md                     # This documentation
└── 📄 DEVELOPMENT_HISTORY.md        # Development history
```

### Module Overview

#### 🎛️ **Admin Module** (`src/admin/`)

Components for survey management and administration:

- **SurveyCreator**: Create and edit surveys using JSON
- **SurveyList**: Display surveys with admin actions (edit, delete, status toggle)
- **SurveyImport**: Import surveys with JSON validation and sample templates

#### 👥 **Client Module** (`src/client/`)

Components for survey participation and viewing results:

- **SurveyRenderer**: Render surveys and collect responses
- **SurveyResults**: Display comprehensive analytics and results
- **useSurveySubmission**: Hook for handling survey submissions

#### ⚙️ **Core Module** (`src/core/`)

Core functionality and utilities:

- **useSurvey**: Main hook for survey management operations
- **survey.types.ts**: TypeScript interfaces and type definitions
- **surveyHelpers.ts**: Utility functions, analytics generation, and validation

#### 🔌 **API Layer** (`src/api/`)

Business logic and data operations:

- **SurveyApi**: Complete API class with all survey operations
- **Storage abstraction**: Works with any storage provider implementation
- **Business logic**: Centralized survey management operations

#### 🎨 **Themes Module** (`src/themes/`)

Styling and theming configuration:

- **default.ts**: Default theme settings and customization options

## Quick Start

### Basic Usage

```tsx
import { SurveyRenderer, SurveyResults } from '@phygitallabs/survey';

// Render a survey
<SurveyRenderer
  surveyId="your-survey-id"
  onComplete={(data) => console.log('Survey completed:', data)}
/>

// View survey results
<SurveyResults
  surveyId="your-survey-id"
  responseCount={10}
  responses={responseData}
/>
```

### Admin Dashboard

```tsx
import { SurveyList, SurveyImport } from '@phygitallabs/survey';

// Survey management with modern UI
<SurveyList
  surveys={surveys}
  onEdit={(id) => handleEdit(id)}
  onDelete={(id) => handleDelete(id)}
  onStatusChange={(id, isActive) => handleStatusChange(id, isActive)}
  onViewResults={(id) => handleViewResults(id)}
  isAdminView={true}
/>

// Import surveys with sample templates
<SurveyImport
  onImport={handleImport}
  sampleSurveyJson={sampleSurvey}
  sampleQuizJson={sampleQuiz}
  onSuccess={handleSuccess}
/>
```

## Components

### SurveyRenderer

Renders surveys with Ant Design styling and collects responses.

```tsx
<SurveyRenderer
  surveyId={string}
  onComplete={(data: Record<string, any>) => void}
  theme?: object
/>
```

### SurveyResults

Displays comprehensive survey analytics with charts and statistics.

```tsx
<SurveyResults
  surveyId={string}
  responseCount?: number
  responses?: SurveyResponse[]
/>
```

### SurveyList

Modern list view for survey management with admin/client modes.

```tsx
<SurveyList
  surveys={Survey[]}
  onEdit?: (id: string) => void
  onDelete?: (id: string) => void
  onStatusChange?: (id: string, isActive: boolean) => void
  onViewResults?: (id: string) => void
  onParticipate?: (id: string) => void
  isAdminView?: boolean
  autoLoad?: boolean
/>
```

### SurveyImport

Import surveys with JSON validation and sample templates.

```tsx
<SurveyImport
  onImport={(json: Record<string, any>) => void}
  sampleSurveyJson?: Record<string, any>
  sampleQuizJson?: Record<string, any>
  onSuccess?: () => void
/>
```

## API Reference

### Core Hooks

```typescript
import { useSurvey } from "@phygitallabs/survey";

const {
  survey,
  responses,
  loading,
  error,
  loadSurvey,
  loadAllSurveys,
  saveSurvey,
  deleteSurvey,
  loadResponses,
  submitResponse,
  getAnalytics,
  createSurvey,
  validateSurvey,
} = useSurvey(surveyId);
```

### API Usage

```typescript
import { SurveyApi, createSurveyApi, SurveyStorage } from "@phygitallabs/survey";

// Create a custom storage implementation
class MyStorage implements SurveyStorage {
  // Implement all required methods
}

// Create API instance
const api = createSurveyApi(new MyStorage());

// Use API directly
const surveys = await api.loadAllSurveys();
const survey = await api.loadSurvey("survey-id");
const id = await api.saveSurvey(surveyData);

// Or use with React hook
const { loadAllSurveys, saveSurvey } = useSurvey(undefined, new MyStorage());

// Demo implementation (IndexedDB) is available in the demo app
// See: apps/tapquest-portal/src/modules/surveys/storage/indexedDB.ts
```

### Analytics Generation

```typescript
import { generateSurveyAnalytics } from "@phygitallabs/survey";

const analytics = generateSurveyAnalytics(survey, responses);
```

## Data Types

### Core Types

#### Survey

```typescript
interface Survey {
  id: string; // UUID primary key
  title: string; // Survey title
  description?: string; // Survey description
  json: Record<string, any>; // SurveyJS JSON configuration
  type: string; // Survey type (survey, quiz, etc.)
  labels?: Record<string, any>; // Flexible metadata (projectId, tags, etc.)
  createdAt: Date; // Creation timestamp
  updatedAt: Date; // Last update timestamp
  isActive: boolean; // Survey status (active/inactive)
}
```

#### SurveyResponse

```typescript
interface SurveyResponse {
  id: string; // UUID primary key
  surveyId: string; // Foreign key to surveys table
  data: Record<string, any>; // Response data (question answers)
  submittedAt: Date; // Submission timestamp
  userId?: string; // Optional user identifier
  labels?: Record<string, any>; // Flexible metadata
  deviceUid?: string; // Optional device identifier
}
```

#### SurveyAnalytics

```typescript
interface SurveyAnalytics {
  id: string; // UUID primary key
  surveyId: string; // Foreign key to surveys table
  totalResponses: number; // Total number of responses
  averageCompletionTime?: number; // Average time to complete (seconds)
  completionRate?: number; // Completion rate percentage (0-100)
  questionStats?: Record<string, any>; // Per-question statistics JSON
  updatedAt: Date; // Last analytics update timestamp
}
```

### Question Statistics

#### QuestionStats

```typescript
interface QuestionStats {
  questionType: string; // Type of question (text, choice, etc.)
  totalAnswers: number; // Number of responses to this question
  answerDistribution?: Record<string, number>; // Answer choice distribution
  averageValue?: number; // Average for numeric questions
  minValue?: number; // Minimum value for numeric questions
  maxValue?: number; // Maximum value for numeric questions
  textResponses?: string[]; // Array of text responses
  averageTextLength?: number; // Average length of text responses
}
```

### Storage Interface

#### SurveyStorage

```typescript
interface SurveyStorage {
  // Initialize the storage
  init(): Promise<void>;

  // Survey operations
  getSurvey(id: string): Promise<Survey | null>;
  getAllSurveys(): Promise<Survey[]>;
  saveSurvey(survey: Survey): Promise<string>;
  deleteSurvey(id: string): Promise<void>;

  // Response operations
  getResponse(id: string): Promise<SurveyResponse | null>;
  getResponsesBySurvey(surveyId: string): Promise<SurveyResponse[]>;
  saveResponse(response: SurveyResponse): Promise<string>;
  deleteResponse(id: string): Promise<void>;
  deleteResponsesBySurvey(surveyId: string): Promise<void>;
}
```

### API Types

#### SurveyApi

```typescript
class SurveyApi {
  constructor(storage: SurveyStorage);

  // Survey management
  loadSurvey(id: string): Promise<Survey | null>;
  loadAllSurveys(): Promise<Survey[]>;
  saveSurvey(survey: Survey): Promise<string>;
  deleteSurvey(id: string): Promise<void>;
  createSurvey(
    title: string,
    description?: string,
    json?: Record<string, any>
  ): Promise<string>;

  // Response management
  loadResponses(surveyId: string): Promise<SurveyResponse[]>;
  submitResponse(surveyId: string, data: Record<string, any>): Promise<string>;
  deleteResponse(id: string): Promise<void>;
  deleteAllResponses(surveyId: string): Promise<void>;

  // Analytics
  getAnalytics(surveyId: string): Promise<SurveyAnalytics>;

  // Validation
  validateSurvey(survey: Survey): boolean;
}
```

### Component Props

#### SurveyRenderer Props

```typescript
interface SurveyRendererProps {
  surveyId: string; // Survey to render
  onComplete?: (data: Record<string, any>) => void; // Completion callback
  theme?: object; // Custom theme object
}
```

#### SurveyResults Props

```typescript
interface SurveyResultsProps {
  surveyId: string; // Survey to display results for
  responseCount?: number; // Total response count
  responses?: SurveyResponse[]; // Response data array
}
```

#### SurveyList Props

```typescript
interface SurveyListProps {
  surveys: Survey[]; // Array of surveys to display
  onEdit?: (id: string) => void; // Edit callback
  onDelete?: (id: string) => void; // Delete callback
  onStatusChange?: (id: string, isActive: boolean) => void; // Status toggle callback
  onViewResults?: (id: string) => void; // View results callback
  onParticipate?: (id: string) => void; // Participate callback
  isAdminView?: boolean; // Admin vs client view mode
  autoLoad?: boolean; // Auto-load surveys
}
```

#### SurveyImport Props

```typescript
interface SurveyImportProps {
  onImport: (json: Record<string, any>) => void; // Import callback
  sampleSurveyJson?: Record<string, any>; // Sample survey template
  sampleQuizJson?: Record<string, any>; // Sample quiz template
  onSuccess?: () => void; // Success callback
}
```

### Hook Return Types

#### useSurvey Hook

```typescript
interface UseSurveyReturn {
  // State
  survey: Survey | null;
  responses: SurveyResponse[];
  loading: boolean;
  error: string | null;

  // Survey operations
  loadSurvey: (id: string) => Promise<void>;
  loadAllSurveys: () => Promise<void>;
  saveSurvey: (survey: Survey) => Promise<string>;
  deleteSurvey: (id: string) => Promise<void>;
  createSurvey: (
    title: string,
    description?: string,
    json?: Record<string, any>
  ) => Promise<string>;

  // Response operations
  loadResponses: (surveyId: string) => Promise<void>;
  submitResponse: (
    surveyId: string,
    data: Record<string, any>
  ) => Promise<string>;
  deleteResponse: (id: string) => Promise<void>;
  deleteAllResponses: (surveyId: string) => Promise<void>;

  // Analytics
  getAnalytics: (surveyId: string) => Promise<SurveyAnalytics>;

  // Validation
  validateSurvey: (survey: Survey) => boolean;
}
```

### Utility Types

#### SurveyJSON

```typescript
type SurveyJSON = Record<string, any>; // SurveyJS JSON configuration
```

#### SurveyTheme

```typescript
interface SurveyTheme {
  cssVariables?: Record<string, string>; // CSS custom properties
  cssClasses?: Record<string, string>; // CSS class mappings
  [key: string]: any; // Additional theme properties
}
```

## Demo Implementation

A complete demo is available in `apps/tapquest-portal/src/modules/surveys/` showcasing:

- **Admin View**: Survey management, import/export, analytics
- **Client View**: Survey participation, response collection
- **Real-time Updates**: Live response tracking and analytics
- **Sample Templates**: Pre-built survey and quiz examples
- **IndexedDB Storage**: Demo storage implementation for local development

### Demo Storage Implementation

The demo includes an IndexedDB storage implementation for local development:

```typescript
// Location: apps/tapquest-portal/src/modules/surveys/storage/indexedDB.ts
import { IndexedDBStorage } from "./storage/indexedDB";

const storage = new IndexedDBStorage();
const { loadAllSurveys, saveSurvey } = useSurvey(undefined, storage);
```

**Note**: This IndexedDB implementation is for demo purposes only. In production, you should implement your own storage provider using the `SurveyStorage` interface.

## Architecture

The survey package implements a modern full-stack architecture with clear separation of concerns:

### 🏗️ **Full-Stack Architecture**

```
┌─────────────────────────────────────────────────────────────┐
│                     Client Applications                     │
├─────────────────────────────────────────────────────────────┤
│  React Components   │   Hooks   │   Theme System   │  API    │
│  - SurveyRenderer   │ useSurvey │   - SurveyJS     │ Client  │
│  - SurveyResults    │ useSubmit │   - Ant Design   │ Layer   │
│  - SurveyList       │           │   - CSS Vars     │         │
├─────────────────────────────────────────────────────────────┤
│                      tRPC API Layer                        │
│  Type-Safe RPC   │   Routers    │  Middleware  │ Validation │
│  - Auto-complete │   - Survey   │  - Auth      │ - Zod      │
│  - Error types   │   - Response │  - Admin     │ - Schema   │
│  - TypeScript    │   - Analytics│  - CORS      │ - Runtime  │
├─────────────────────────────────────────────────────────────┤
│                    Database Layer (PostgreSQL)              │
│  ORM Integration │   Schema     │  Migrations  │ Relations  │
│  - Drizzle ORM   │   - Surveys  │  - Versioned │ - Foreign  │
│  - Type Safety   │   - Responses│  - Automated │   Keys     │
│  - Query Builder │   - Analytics│  - Rollback  │ - Cascade  │
└─────────────────────────────────────────────────────────────┘
```

### 🔄 **Data Flow**

1. **Client Request**: React components call tRPC procedures
2. **Type Validation**: Zod schemas validate input/output
3. **Authentication**: JWT middleware checks permissions
4. **Database Operations**: Drizzle ORM executes type-safe queries
5. **Response**: Type-safe data flows back to components

### 🔌 **API-First Design**

The package supports both direct API usage and React integration:

- **tRPC Procedures**: Type-safe server functions with automatic client generation
- **SurveyApi Class**: Client-side abstraction for storage-agnostic operations
- **React Hooks**: State management wrappers around API calls
- **Dual Exports**: Separate client (`./index`) and server (`./server`) entry points

### **Key Architectural Benefits**

- **Type Safety**: End-to-end TypeScript with runtime validation
- **Scalability**: Production-ready PostgreSQL with proper indexing
- **Security**: JWT authentication, input validation, SQL injection prevention
- **Performance**: Optimized queries, pagination, connection pooling
- **Maintainability**: Clear separation of concerns, modular structure
- **Testability**: Independent layers can be tested in isolation

## Features in Detail

### 🎨 Modern UI Components

- **Ant Design v5**: Professional, accessible, and responsive components
- **Statistics Cards**: Visual representation of survey metrics
- **Data Tables**: Clean, sortable tables for response analysis
- **Progress Indicators**: Loading states and completion tracking
- **Alert System**: User-friendly error and success notifications

### 📊 Advanced Analytics

- **Response Distribution**: Visual breakdown of answer choices
- **Numeric Statistics**: Min, max, average, and median calculations
- **Text Analysis**: Response count and average length metrics
- **Completion Tracking**: Real-time completion rate monitoring
- **Question-level Insights**: Detailed statistics per question type

### 🔄 Real-time Features

- **Live Updates**: Response counts update immediately after submission
- **Instant Analytics**: Results generated on-demand with fresh data
- **Status Management**: Toggle survey active/inactive states
- **Response Tracking**: Monitor survey participation in real-time

### 📱 Responsive Design

- **Mobile-First**: Optimized for all screen sizes
- **Touch-Friendly**: Proper touch targets and interactions
- **Flexible Layout**: Adapts to different viewport sizes
- **Accessible**: WCAG compliant components and interactions

## Server Setup

### Database Configuration

The package uses PostgreSQL with Drizzle ORM for data persistence:

```typescript
// Configure your database connection
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

const client = postgres(process.env.DATABASE_URL);
const db = drizzle(client);
```

### Running Migrations

```bash
# Generate migration files
npm run db:generate

# Apply migrations
npm run db:migrate

# Open Drizzle Studio (database GUI)
npm run db:studio
```

### tRPC Server Integration

```typescript
import { appRouter, createContext } from '@phygitallabs/survey/server';
import { createHTTPServer } from '@trpc/server/adapters/standalone';

const server = createHTTPServer({
  router: appRouter,
  createContext,
});

server.listen(3001);
```

### Environment Variables

```bash
# Required environment variables
DATABASE_URL=postgresql://user:password@localhost:5432/survey_db
JWT_SECRET=your-jwt-secret-key
ADMIN_JWT_SECRET=your-admin-jwt-secret

# Optional
NODE_ENV=development
PORT=3001
```

## Client Setup

### tRPC Client Configuration

```typescript
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@phygitallabs/survey/server';

const trpc = createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3001/trpc',
    }),
  ],
});

// Usage
const surveys = await trpc.survey.getAll.query({ page: 1, limit: 10 });
```

### React Query Integration

```typescript
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@phygitallabs/survey/server';

const trpc = createTRPCReact<AppRouter>();
const queryClient = new QueryClient();

function App() {
  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        {/* Your app components */}
      </QueryClientProvider>
    </trpc.Provider>
  );
}
```

## Development

See [DEVELOPMENT_HISTORY.md](./DEVELOPMENT_HISTORY.md) for the development roadmap and history.

## License

This package uses the free version of SurveyJS, which is licensed under MIT.