{"_id":"@aisahub/bi-ui","_rev":"2-e03e4c1a3bfbffc73d7bc6f7b85781a8","name":"@aisahub/bi-ui","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aisahub/bi-ui","version":"0.1.0","author":{"name":"Geared AI"},"license":"ISC","_id":"@aisahub/bi-ui@0.1.0","maintainers":[{"name":"zakuby","email":"muhammad.yaqub.baisa@gmail.com"},{"name":"fathanabds","email":"abdulshodiqfathan@gmail.com"}],"dist":{"shasum":"8fcfe350707919a46873ea0a1b924ec09c8efa31","tarball":"https://registry.npmjs.org/@aisahub/bi-ui/-/bi-ui-0.1.0.tgz","fileCount":30,"integrity":"sha512-iZbS53yiEwPKekKDj+ZcsIG6gQa3kXpXjzIgQCKUTkeoKjIVxyFdtY5PCEb/f0NKqZUb19LMHyrrLy8bzdcyUg==","signatures":[{"sig":"MEQCIH6LnX1uOHlg/XhPBfdkPt7FJz49cUuef7BSqZpG5NZ4AiB2RivxX0J6u09t0npe1UeWRb/PJPlFZ5kdpslAX0ebDQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16357391},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"4c8d80ff8b158ae01bbea4cb752163a06219a7cd","scripts":{"test":"echo \"Error: no test specified\" && exit 1","rollup":"rollup -c --bundleConfigAsCjs"},"_npmUser":{"name":"fathanabds","email":"abdulshodiqfathan@gmail.com"},"_npmVersion":"11.6.4","description":"Smart query form and chart component","directories":{},"_nodeVersion":"22.14.0","dependencies":{"axios":"^1.10.0","rollup":"^4.45.0","echarts":"^6.0.0","lucide-react":"^0.468.0","tailwind-merge":"^3.3.1","react-hook-form":"^7.60.0","echarts-for-react":"^3.0.5","@radix-ui/react-slot":"^1.2.3","@radix-ui/react-label":"^2.1.7","class-variance-authority":"^0.7.1"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19.1.0","tslib":"^2.8.1","typescript":"^5.8.3","@types/react":"^19.1.8","@emotion/react":"^11.14.0","@emotion/styled":"^11.14.1","rollup-plugin-dts":"^6.2.1","@rollup/plugin-json":"^6.1.0","@rollup/plugin-alias":"^5.1.1","@rollup/plugin-terser":"^0.4.4","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-typescript":"^12.1.4","@rollup/plugin-node-resolve":"^16.0.1","rollup-plugin-peer-deps-external":"^2.2.4"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@emotion/react":"^11.0.0","@emotion/styled":"^11.0.0"},"_npmOperationalInternal":{"tmp":"tmp/bi-ui_0.1.0_1766386696036_0.13036945775776698","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aisahub/bi-ui","version":"0.2.0","description":"Smart query form and chart component with Firebase real-time updates","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","scripts":{"test":"echo \"Error: no test specified\" && exit 1","rollup":"rollup -c --bundleConfigAsCjs"},"author":{"name":"Geared AI"},"license":"ISC","peerDependencies":{"@emotion/react":"^11.0.0","@emotion/styled":"^11.0.0","react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"devDependencies":{"@emotion/react":"^11.14.0","@emotion/styled":"^11.14.1","@rollup/plugin-alias":"^5.1.1","@rollup/plugin-commonjs":"^28.0.6","@rollup/plugin-json":"^6.1.0","@rollup/plugin-node-resolve":"^16.0.1","@rollup/plugin-terser":"^0.4.4","@rollup/plugin-typescript":"^12.1.4","@types/react":"^19.1.8","react":"^19.1.0","rollup-plugin-dts":"^6.2.1","rollup-plugin-peer-deps-external":"^2.2.4","tslib":"^2.8.1","typescript":"^5.8.3"},"dependencies":{"@radix-ui/react-label":"^2.1.7","@radix-ui/react-slot":"^1.2.3","axios":"^1.10.0","class-variance-authority":"^0.7.1","echarts":"^6.0.0","echarts-for-react":"^3.0.5","firebase":"^12.0.0","lucide-react":"^0.468.0","react-hook-form":"^7.60.0","rollup":"^4.45.0","tailwind-merge":"^3.3.1"},"gitHead":"08d823ca2a75a4182cd1db74b245318e02582d1a","_id":"@aisahub/bi-ui@0.2.0","_nodeVersion":"22.14.0","_npmVersion":"11.6.4","dist":{"integrity":"sha512-Wyf79gCIKVGsqff9BZSGA8jZJ9THVkGQHVd0XvRH9qvCJB1b16Yb69lmSBOrE0mzs9KjoK+s9YIKKwqyCkQFQg==","shasum":"bdd37285f47b4810b3522b5b1dbc581ce80eda46","tarball":"https://registry.npmjs.org/@aisahub/bi-ui/-/bi-ui-0.2.0.tgz","fileCount":35,"unpackedSize":18782539,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF1JgEQYdxPq4Z3vPS2uqdkaXrPxeD88NB//moUBBJBUAiEAsLYVwHRrrnTt6W7rCKrfy9XJ2wAPofv0xxS1tBgwuZg="}]},"_npmUser":{"name":"fathanabds","email":"abdulshodiqfathan@gmail.com"},"directories":{},"maintainers":[{"name":"zakuby","email":"muhammad.yaqub.baisa@gmail.com"},{"name":"fathanabds","email":"abdulshodiqfathan@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bi-ui_0.2.0_1767840436797_0.2908227932211942"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-22T06:58:15.938Z","modified":"2026-01-08T02:47:17.359Z","0.1.0":"2025-12-22T06:58:16.343Z","0.2.0":"2026-01-08T02:47:17.124Z"},"author":{"name":"Geared AI"},"license":"ISC","description":"Smart query form and chart component with Firebase real-time updates","maintainers":[{"name":"zakuby","email":"muhammad.yaqub.baisa@gmail.com"},{"name":"fathanabds","email":"abdulshodiqfathan@gmail.com"}],"readme":"# aisahub-bi-ui\n\nReact component library for intelligent data visualization and query form management. Build AI-powered business intelligence interfaces with pre-built components for data upload, natural language queries, and interactive chart generation.\n\n## Features\n\n- **Smart Query Forms** - User input with optional file upload and chart type selection\n- **60+ Chart Types** - Line, Bar, Pie, and Scatter charts with multiple subtypes\n- **Multi-Chart Dashboard** - Responsive grid layout with individual chart management\n- **Real-time Updates** - Firebase integration for live chart updates and progress tracking\n- **Chart Management** - CRUD operations with refresh, delete, and caching\n- **CSS Theme Variables** - Full customization via CSS custom properties\n- **TypeScript Support** - Complete type definitions included\n- **Next.js Compatible** - Works with App Router and Pages Router\n- **Accessible** - ARIA attributes, keyboard navigation, and focus management\n\n## Installation\n\n```bash\nnpm install aisahub-bi-ui\n# or\nyarn add aisahub-bi-ui\n```\n\n### Peer Dependencies\n\n```bash\nnpm install react react-dom @emotion/react @emotion/styled\n```\n\n| Package | Version |\n|---------|---------|\n| react | ^18.0.0 or ^19.0.0 |\n| react-dom | ^18.0.0 or ^19.0.0 |\n| @emotion/react | ^11.0.0 |\n| @emotion/styled | ^11.0.0 |\n\n## Quick Start\n\n```tsx\nimport {\n  QueryForm,\n  ChartDisplay,\n  useChartData,\n  PageHeader\n} from 'aisahub-bi-ui';\n\nfunction App() {\n  const {\n    chartOptions,\n    isLoading,\n    error,\n    submitQuery,\n    resetChart\n  } = useChartData();\n\n  const handleSubmit = (data) => {\n    submitQuery(data, '/api/generate-chart');\n  };\n\n  return (\n    <div>\n      <PageHeader\n        title=\"Data Visualization\"\n        subtitle=\"Describe what you want to visualize\"\n      />\n\n      {!chartOptions ? (\n        <QueryForm\n          onSubmit={handleSubmit}\n          isLoading={isLoading}\n          error={error}\n        />\n      ) : (\n        <ChartDisplay\n          options={chartOptions}\n          onReset={resetChart}\n        />\n      )}\n    </div>\n  );\n}\n```\n\n## API Configuration\n\nThe `useChartData` hook communicates with your backend API to generate charts. Configure the base URL when initializing:\n\n```tsx\n// Default: http://localhost:8000\nconst { submitQuery } = useChartData();\n\n// Custom base URL\nconst { submitQuery } = useChartData('https://your-api.com');\n```\n\n### Request Format\n\nThe hook sends a `FormData` POST request with:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `userRequest` | string | Natural language query (chart type appended if selected) |\n| `dbDump` | File (optional) | Uploaded data file (.sql, .csv, .json, .txt) |\n\n### Response Format\n\nYour API should return:\n\n```json\n{\n  \"echarts_config\": { /* ECharts option object */ },\n  \"intent\": {\n    \"intent_complete\": true,\n    \"metrics\": [\"revenue\", \"sales\"],\n    \"dimensions\": [\"month\", \"region\"],\n    \"filters\": {},\n    \"chart_type\": \"bar\",\n    \"missing\": [],\n    \"clarification_needed\": \"\"\n  }\n}\n```\n\nWhen `intent_complete` is `false`, the `clarification_needed` field contains a question for the user, which can be displayed using the `FeedbackForm` component.\n\n### Custom Headers & Abort Signal\n\n```tsx\nsubmitQuery(formData, '/api/chart', {\n  headers: { 'Authorization': 'Bearer token' },\n  signal: abortController.signal\n});\n```\n\n## Components\n\n### QueryForm\n\nHigh-level query form with optional file upload and chart type selection.\n\n```tsx\n<QueryForm\n  onSubmit={(data) => handleSubmit(data)}\n  isLoading={false}\n  includeDbDump={true}\n  includeChartTypeSelector={true}\n  progress=\"Generating chart...\"\n  error={null}\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `onSubmit` | `(data: FormData) => void` | required | Form submission handler |\n| `isLoading` | `boolean` | required | Shows loading state |\n| `includeDbDump` | `boolean` | `false` | Show file upload field |\n| `includeChartTypeSelector` | `boolean` | `true` | Show chart type selector |\n| `progress` | `string` | `''` | Progress message during loading |\n| `error` | `string \\| null` | `null` | Error message to display |\n| `className` | `string` | - | Container class |\n| `classNames` | `BaseFormClassNames` | - | Element-level classes |\n\n### ChartDisplay\n\nSingle chart viewer with reset functionality.\n\n```tsx\n<ChartDisplay\n  options={echartsOptions}\n  onReset={() => setChartOptions(null)}\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `options` | `EChartsOption` | required | ECharts configuration object |\n| `onReset` | `() => void` | required | Reset button handler |\n| `className` | `string` | - | Container class |\n| `classNames` | `ChartDisplayClassNames` | - | Element-level classes |\n\n### MultiChartDisplay\n\nDashboard with multiple charts in a responsive grid.\n\n```tsx\n<MultiChartDisplay\n  charts={[\n    {\n      id: '1',\n      title: 'Revenue by Month',\n      options: echartsOptions,\n      size: 'medium',\n      query: 'SELECT * FROM sales'\n    }\n  ]}\n  onReset={() => setShowForm(true)}\n  onDelete={(id) => removeChart(id)}\n  title=\"Analytics Dashboard\"\n  columns={2}\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `charts` | `ChartItem[]` | required | Array of chart configurations |\n| `onReset` | `() => void` | required | \"New Chart\" button handler |\n| `onDelete` | `(id: string) => void` | required | Delete chart handler |\n| `title` | `string` | `'Dashboard'` | Dashboard title |\n| `columns` | `number` | `2` | Grid column count |\n| `className` | `string` | - | Container class |\n| `classNames` | `MultiChartDisplayClassNames` | - | Element-level classes |\n\n### FeedbackForm\n\nForm for AI clarification requests when intent is incomplete.\n\n```tsx\n<FeedbackForm\n  onSubmit={(data) => handleClarification(data)}\n  isLoading={false}\n  intent={intentData}\n  includeDbDump={false}\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `onSubmit` | `(data: FormData) => void` | required | Form submission handler |\n| `isLoading` | `boolean` | required | Shows loading state |\n| `intent` | `Intent \\| null` | required | Intent object with clarification question |\n| `includeDbDump` | `boolean` | `false` | Show file upload field |\n| `progress` | `string` | `''` | Progress message |\n| `error` | `string \\| null` | `null` | Error message |\n\n### PageHeader\n\nCustomizable page header with title and subtitle.\n\n```tsx\n<PageHeader\n  title=\"Data Visualization Tool\"\n  subtitle=\"Upload your data and describe what you want to visualize\"\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `title` | `string` | `'Data Visualization Tool'` | Main heading |\n| `subtitle` | `string` | `'Upload your data...'` | Subheading text |\n| `className` | `string` | - | Container class |\n| `classNames` | `PageHeaderClassNames` | - | Element-level classes |\n\n### AnimatedModal\n\nAccessible modal dialog with animations.\n\n```tsx\n<AnimatedModal\n  isOpen={showModal}\n  onClose={() => setShowModal(false)}\n  title=\"Chart Details\"\n>\n  <p>Modal content here</p>\n</AnimatedModal>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `isOpen` | `boolean` | required | Controls visibility |\n| `onClose` | `() => void` | required | Close handler |\n| `title` | `string` | - | Modal title |\n| `children` | `ReactNode` | - | Modal content |\n| `className` | `string` | - | Modal container class |\n| `classNames` | `AnimatedModalClassNames` | - | Element-level classes |\n\n### LoadingDisplay\n\nAnimated loading spinner.\n\n```tsx\n<LoadingDisplay />\n```\n\n### ErrorDisplay\n\nError message with retry button.\n\n```tsx\n<ErrorDisplay\n  message=\"Failed to generate chart\"\n  onRetry={() => refetch()}\n/>\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `message` | `string` | required | Error message text |\n| `onRetry` | `() => void` | - | Optional retry handler |\n\n## Hooks\n\n### useChartData\n\nHook for managing chart data submission and state.\n\n```tsx\nconst {\n  chartOptions,      // EChartsOption | null\n  isLoading,         // boolean\n  error,             // string | null\n  addChart,          // boolean\n  setAddChart,       // Dispatch<SetStateAction<boolean>>\n  isIntentComplete,  // boolean\n  intent,            // Intent | null\n  submitQuery,       // (data, url, options?) => Promise<void>\n  resetChart,        // () => void\n} = useChartData(baseURL?: string);\n```\n\n### useChartManagement\n\nHook for managing chart CRUD operations, caching, and refresh functionality.\n\n```tsx\nimport { useChartManagement } from 'aisahub-bi-ui';\n\nconst {\n  charts,              // ChartData[] - Array of charts\n  setCharts,           // Setter for charts state\n  isRendering,         // boolean - Loading cached charts\n  isRefreshing,        // boolean - Refreshing all charts\n  setIsRefreshing,     // Setter for refresh state\n  chartsMapRef,        // Ref<Map<number, ChartData>> - Chart lookup map\n  updatingChartsRef,   // Ref<Set<number>> - Set of updating chart IDs\n  chartsRequestId,     // string | null - Firebase request ID\n  fetchCachedCharts,   // () => Promise<void> - Fetch charts from backend\n  triggerRefresh,      // () => Promise<void> - Refresh all charts\n  deleteChart,         // (id: string) => Promise<void> - Delete a chart\n} = useChartManagement({\n  baseURL: 'https://your-api.com',\n  onError: (error) => console.error(error),\n  getAuthToken: () => sessionStorage.getItem('token')\n});\n```\n\n**Configuration Options:**\n\n| Option | Type | Required | Description |\n|--------|------|----------|-------------|\n| `baseURL` | `string` | Yes | Backend API base URL |\n| `onError` | `(error: Error) => void` | No | Error handler callback |\n| `getAuthToken` | `() => string \\| null` | No | Function to retrieve auth token |\n\n### useFirebaseStatus\n\nSubscribe to Firebase status updates for request processing progress.\n\n```tsx\nimport { useFirebaseStatus, initializeFirebase } from 'aisahub-bi-ui';\n\n// Initialize Firebase first\nconst firebaseDb = initializeFirebase({\n  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,\n  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,\n  databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DB_URL,\n  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,\n  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,\n  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,\n  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,\n});\n\n// Use the hook\nconst status = useFirebaseStatus(requestId, firebaseDb);\n// status: \"Processing query...\" | \"Generating chart...\" | etc.\n```\n\n### useFirebaseChartUpdates\n\nSubscribe to real-time Firebase chart updates during refresh operations.\n\n```tsx\nimport { useFirebaseChartUpdates } from 'aisahub-bi-ui';\n\nuseFirebaseChartUpdates({\n  chartsRequestId,      // Request ID from backend\n  chartsMapRef,         // Ref to Map<number, ChartData>\n  updatingChartsRef,    // Ref to Set<number>\n  setCharts,            // State setter\n  onComplete: () => {   // Called when all charts updated\n    setIsRefreshing(false);\n  },\n  firebaseDb,           // Optional Firebase DB instance\n});\n```\n\n## Firebase Setup\n\nThe library includes Firebase hooks for real-time updates. To use them:\n\n### 1. Install Firebase SDK\n\nFirebase is included as a dependency, no additional installation needed.\n\n### 2. Environment Variables\n\nAdd Firebase configuration to your `.env.local`:\n\n```env\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_API_KEY=your-api-key\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_AUTH_DOMAIN=your-project.firebaseapp.com\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_DB_URL=https://your-project.firebasedatabase.app\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_PROJECT_ID=your-project-id\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_STORAGE_BUCKET=your-project.appspot.com\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_MESSAGING_SENDER_ID=your-sender-id\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_APP_ID=your-app-id\nNEXT_PUBLIC_FIREBASE_QUERYVIZ_MEASUREMENT_ID=G-XXXXXXXXXX\n```\n\n### 3. Initialize Firebase\n\n```tsx\nimport { initializeFirebase } from 'aisahub-bi-ui';\n\n// Option 1: Auto-read from environment variables\nconst firebaseDb = initializeFirebase();\n\n// Option 2: Pass config explicitly\nconst firebaseDb = initializeFirebase({\n  apiKey: \"...\",\n  authDomain: \"...\",\n  databaseURL: \"...\",\n  projectId: \"...\",\n  storageBucket: \"...\",\n  messagingSenderId: \"...\",\n  appId: \"...\",\n}, 'uniqueAppName');\n```\n\n### 4. Firebase Database Rules\n\nSet up Realtime Database with these rules:\n\n```json\n{\n  \"rules\": {\n    \"status\": {\n      \"$requestId\": {\n        \".read\": true,\n        \".write\": \"auth != null\"\n      }\n    },\n    \"charts\": {\n      \"$requestId\": {\n        \".read\": true,\n        \".write\": \"auth != null\"\n      }\n    }\n  }\n}\n```\n\n## Complete Integration Example\n\n```tsx\n'use client';\n\nimport {\n  QueryForm,\n  MultiChartDisplay,\n  AnimatedModal,\n  FeedbackForm,\n  useChartData,\n  useChartManagement,\n  useFirebaseStatus,\n  useFirebaseChartUpdates,\n  initializeFirebase,\n} from 'aisahub-bi-ui';\nimport { useEffect, useState } from 'react';\n\nexport default function BIDashboard() {\n  const baseUrl = 'https://your-api.com';\n\n  // Initialize Firebase\n  const firebaseDb = initializeFirebase();\n\n  // Chart submission and feedback flow\n  const {\n    isLoading,\n    submitQuery,\n    resetChart,\n    isIntentComplete,\n    intent,\n    addChart,\n    setAddChart,\n    error,\n  } = useChartData(baseUrl);\n\n  const [requestId, setRequestId] = useState<string | null>(null);\n  const status = useFirebaseStatus(requestId, firebaseDb);\n\n  // Chart management\n  const {\n    charts,\n    setCharts,\n    isRendering,\n    chartsMapRef,\n    updatingChartsRef,\n    chartsRequestId,\n    fetchCachedCharts,\n    triggerRefresh,\n    deleteChart,\n    setIsRefreshing,\n  } = useChartManagement({\n    baseURL: baseUrl,\n    onError: (error) => console.error(error),\n    getAuthToken: () => sessionStorage.getItem('token'),\n  });\n\n  // Real-time chart updates\n  useFirebaseChartUpdates({\n    chartsRequestId,\n    chartsMapRef,\n    updatingChartsRef,\n    setCharts,\n    onComplete: () => setIsRefreshing(false),\n    firebaseDb,\n  });\n\n  // Initial load\n  useEffect(() => {\n    fetchCachedCharts().then(() => triggerRefresh());\n  }, []);\n\n  // Handle new submission\n  const handleSubmit = (data) => {\n    const newRequestId = crypto.randomUUID();\n    setRequestId(newRequestId);\n    submitQuery(data, '/chart-requests', {\n      headers: {\n        'Authorization': 'Bearer ' + sessionStorage.getItem('token'),\n        'X-Request-ID': newRequestId,\n      },\n    });\n  };\n\n  return (\n    <div>\n      <MultiChartDisplay\n        charts={charts}\n        onReset={resetChart}\n        onDelete={(id) => deleteChart(id)}\n        title=\"Dashboard\"\n        columns={2}\n      />\n\n      {addChart && (\n        <AnimatedModal isOpen onClose={() => setAddChart(false)}>\n          <QueryForm\n            onSubmit={handleSubmit}\n            isLoading={isLoading}\n            progress={status}\n            error={error}\n          />\n        </AnimatedModal>\n      )}\n\n      {!isIntentComplete && intent && (\n        <AnimatedModal isOpen onClose={() => resetChart()}>\n          <FeedbackForm\n            onSubmit={handleSubmit}\n            isLoading={isLoading}\n            intent={intent}\n            progress={status}\n          />\n        </AnimatedModal>\n      )}\n    </div>\n  );\n}\n```\n\n## TypeScript Types\n\n```tsx\nimport type {\n  FormData,\n  ChartApiResponse,\n  ChartItem,\n  Intent,\n  ChartCategory,\n  UseChartDataReturn,\n  SubmitQueryOptions,\n  FileValidationConfig\n} from 'aisahub-bi-ui';\n\n// Chart categories\ntype ChartCategory = 'line' | 'bar' | 'pie' | 'scatter';\n\n// Form data structure\ninterface FormData {\n  user_request: string;\n  db_dump: FileList | null;\n  chart_type?: ChartCategory;\n  chart_sub_type?: string;\n}\n\n// Chart item for dashboard\ninterface ChartItem {\n  id: string;\n  title: string;\n  options: EChartsOption;\n  size?: 'small' | 'medium' | 'large';\n  query: string;\n  isUpdating?: boolean;\n  updateError?: string;\n}\n\n// Intent from API\ninterface Intent {\n  intent_complete: boolean;\n  metrics: string[];\n  dimensions: string[];\n  filters: object;\n  chart_type: string;\n  missing: string[];\n  clarification_needed: string;\n}\n```\n\n## Theming\n\nAll components use CSS custom properties with the `--bi-` prefix. Override them in your global CSS:\n\n```css\n:root {\n  /* Primary colors */\n  --bi-primary: hsl(222.2 47.4% 11.2%);\n  --bi-primary-hover: hsl(222.2 47.4% 20%);\n  --bi-primary-foreground: hsl(210 40% 98%);\n\n  /* Background & Foreground */\n  --bi-background: hsl(0 0% 100%);\n  --bi-foreground: hsl(222.2 84% 4.9%);\n\n  /* Semantic colors */\n  --bi-success: hsl(142.1 76.2% 36.3%);\n  --bi-error: hsl(0 84.2% 60.2%);\n  --bi-destructive: hsl(0 84.2% 60.2%);\n\n  /* Border & Input */\n  --bi-border: hsl(214.3 31.8% 91.4%);\n  --bi-input: hsl(214.3 31.8% 91.4%);\n  --bi-ring: hsl(222.2 84% 4.9%);\n\n  /* Muted colors */\n  --bi-muted: hsl(210 40% 96.1%);\n  --bi-muted-foreground: hsl(215.4 16.3% 46.9%);\n\n  /* Card colors */\n  --bi-card: hsl(0 0% 100%);\n  --bi-card-foreground: hsl(222.2 84% 4.9%);\n\n  /* Border radius */\n  --bi-radius: 0.5rem;\n\n  /* Typography */\n  --bi-text-sm: 0.875rem;\n  --bi-text-base: 1rem;\n  --bi-text-lg: 1.125rem;\n  --bi-font-sans: ui-sans-serif, system-ui, sans-serif;\n\n  /* Shadows */\n  --bi-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --bi-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n  --bi-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);\n\n  /* Transitions */\n  --bi-transition-fast: 150ms ease-in-out;\n  --bi-transition-normal: 200ms ease-in-out;\n}\n```\n\n### Dark Mode\n\n```css\n.dark {\n  --bi-background: hsl(222.2 84% 4.9%);\n  --bi-foreground: hsl(210 40% 98%);\n  --bi-card: hsl(222.2 84% 4.9%);\n  --bi-card-foreground: hsl(210 40% 98%);\n  --bi-border: hsl(217.2 32.6% 17.5%);\n  --bi-muted: hsl(217.2 32.6% 17.5%);\n  --bi-muted-foreground: hsl(215 20.2% 65.1%);\n}\n```\n\n## Chart Types Reference\n\n### Line Charts (15 subtypes)\n\n| Value | Label |\n|-------|-------|\n| `line-simple` | Basic Line |\n| `line-smooth` | Smoothed Line |\n| `area-basic` | Basic Area |\n| `line-stack` | Stacked Line |\n| `area-stack` | Stacked Area |\n| `area-stack-gradient` | Gradient Stacked Area |\n| `line-step` | Step Line |\n| `line-marker` | Line with Markers |\n| `line-gradient` | Line Gradient |\n| `line-style` | Line Style |\n| `area-pieces` | Area Pieces |\n| `line-log` | Log Axis |\n| `line-polar` | Polar Line |\n| `bump-chart` | Bump Chart |\n| `confidence-band` | Confidence Band |\n\n### Bar Charts (14 subtypes)\n\n| Value | Label |\n|-------|-------|\n| `bar-simple` | Basic Bar |\n| `bar-stack` | Stacked Bar |\n| `bar-y-category` | Horizontal Bar |\n| `bar-waterfall` | Waterfall |\n| `bar-gradient` | Gradient Bar |\n| `bar-negative` | Negative Values |\n| `bar-label-rotation` | Label Rotation |\n| `bar-y-category-stack` | Horizontal Stacked |\n| `bar-polar-stack` | Polar Stacked |\n| `bar-race` | Bar Race |\n| `bar-background` | Bar with Background |\n| `mix-line-bar` | Mixed Line & Bar |\n| `bar-drilldown` | Drilldown |\n| `polar-roundCap` | Rounded Polar |\n\n### Pie Charts (13 subtypes)\n\n| Value | Label |\n|-------|-------|\n| `pie-simple` | Basic Pie |\n| `pie-doughnut` | Doughnut |\n| `pie-roseType` | Nightingale |\n| `pie-half-donut` | Half Doughnut |\n| `pie-nest` | Nested Pies |\n| `pie-borderRadius` | Rounded Doughnut |\n| `pie-padAngle` | Padded Pie |\n| `pie-pattern` | Textured Pie |\n| `pie-legend` | Scrollable Legend |\n| `pie-rich-text` | Rich Text Label |\n| `pie-labelLine-adjust` | Label Line Adjust |\n| `pie-custom` | Custom Pie |\n| `pie-roseType-simple` | Simple Nightingale |\n\n### Scatter Charts (12 subtypes)\n\n| Value | Label |\n|-------|-------|\n| `scatter-simple` | Basic Scatter |\n| `scatter-effect` | Effect Scatter |\n| `scatter-weight` | Bubble |\n| `bubble-gradient` | Gradient Bubble |\n| `scatter-punchCard` | Punch Card |\n| `scatter-single-axis` | Single Axis |\n| `scatter-linear-regression` | Linear Regression |\n| `scatter-exponential-regression` | Exponential Regression |\n| `scatter-clustering` | Clustering |\n| `scatter-anscombe-quartet` | Anscombe Quartet |\n| `scatter-aqi-color` | AQI Color |\n| `scatter-matrix` | Scatter Matrix |\n\n## Next.js Integration\n\n### App Router\n\nAll components include the `'use client'` directive and work out of the box:\n\n```tsx\n// app/dashboard/page.tsx\nimport { QueryForm, ChartDisplay, useChartData } from 'aisahub-bi-ui';\n\nexport default function DashboardPage() {\n  // Components are already client components\n  return <Dashboard />;\n}\n```\n\n### With Server Components\n\nWrap the library components in a client component:\n\n```tsx\n// components/ChartWidget.tsx\n'use client';\n\nimport { QueryForm, useChartData } from 'aisahub-bi-ui';\n\nexport function ChartWidget() {\n  const { submitQuery, isLoading } = useChartData();\n  return <QueryForm onSubmit={submitQuery} isLoading={isLoading} />;\n}\n```\n\n## Accessibility\n\n- **Semantic HTML**: Proper heading hierarchy, form labels, and landmarks\n- **ARIA attributes**: `aria-label`, `aria-describedby`, `aria-invalid`, `aria-live`\n- **Keyboard navigation**: Full Tab support, Escape to close modals\n- **Focus management**: Focus trapping in modals, focus restoration on close\n- **Screen reader support**: Loading announcements via `aria-live` regions\n\n## License\n\nISC\n\n## Author\n\nGeared AI\n","readmeFilename":"README.md"}