create-bluecopa-react-app 1.0.41 → 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/templates/latest/.claude/settings.local.json +56 -0
  4. package/templates/latest/.env.example +8 -0
  5. package/templates/latest/Agent.md +598 -775
  6. package/templates/latest/CLAUDE.md +759 -0
  7. package/templates/latest/README.md +11 -2
  8. package/templates/latest/app/app.css +292 -85
  9. package/templates/latest/app/app.tsx +48 -39
  10. package/templates/latest/app/components/bluecopa-logo.tsx +20 -0
  11. package/templates/latest/app/components/charts/bar-chart.tsx +132 -0
  12. package/templates/latest/app/components/charts/base-chart.tsx +149 -0
  13. package/templates/latest/app/components/charts/chart-provider.tsx +71 -0
  14. package/templates/latest/app/components/charts/chart-theme.ts +262 -0
  15. package/templates/latest/app/components/charts/chart-utils.ts +142 -0
  16. package/templates/latest/app/components/charts/donut-chart.tsx +110 -0
  17. package/templates/latest/app/components/charts/index.ts +5 -0
  18. package/templates/latest/app/components/layouts/app-layout.tsx +22 -0
  19. package/templates/latest/app/components/layouts/app-sidebar.tsx +88 -0
  20. package/templates/latest/app/components/layouts/nav-main.tsx +50 -0
  21. package/templates/latest/app/components/layouts/nav-user.tsx +38 -0
  22. package/templates/latest/app/components/layouts/site-header.tsx +93 -0
  23. package/templates/latest/app/components/loading-screen.tsx +41 -0
  24. package/templates/latest/app/components/ui/ag-grid-table.tsx +79 -0
  25. package/templates/latest/app/components/ui/button.tsx +23 -23
  26. package/templates/latest/app/components/ui/card.tsx +20 -20
  27. package/templates/latest/app/components/ui/dropdown-menu.tsx +54 -49
  28. package/templates/latest/app/components/ui/input.tsx +8 -8
  29. package/templates/latest/app/components/ui/label.tsx +8 -8
  30. package/templates/latest/app/components/ui/separator.tsx +7 -7
  31. package/templates/latest/app/components/ui/sheet.tsx +43 -32
  32. package/templates/latest/app/components/ui/sidebar.tsx +240 -235
  33. package/templates/latest/app/components/ui/skeleton.tsx +4 -4
  34. package/templates/latest/app/components/ui/sonner.tsx +6 -9
  35. package/templates/latest/app/components/ui/tabs.tsx +15 -15
  36. package/templates/latest/app/components/ui/tooltip.tsx +18 -12
  37. package/templates/latest/app/constants/index.ts +31 -0
  38. package/templates/latest/app/contexts/app-context.tsx +201 -0
  39. package/templates/latest/app/hooks/use-mobile.ts +13 -12
  40. package/templates/latest/app/main.tsx +1 -1
  41. package/templates/latest/app/pages/dashboard.tsx +246 -0
  42. package/templates/latest/app/pages/payments.tsx +182 -0
  43. package/templates/latest/app/pages/settings.tsx +128 -0
  44. package/templates/latest/app/routes/index.tsx +19 -0
  45. package/templates/latest/app/single-spa.tsx +69 -186
  46. package/templates/latest/app/types/index.ts +37 -0
  47. package/templates/latest/app/utils/ag-grid-datasource.ts +63 -0
  48. package/templates/latest/app/utils/ag-grid-license.ts +12 -0
  49. package/templates/latest/app/utils/ag-grid-theme.ts +9 -0
  50. package/templates/latest/app/utils/component-style.ts +7 -0
  51. package/templates/latest/app/utils/style-drivers.ts +24 -0
  52. package/templates/latest/app/utils/utils.ts +10 -0
  53. package/templates/latest/components.json +3 -3
  54. package/templates/latest/index.html +30 -2
  55. package/templates/latest/package-lock.json +2717 -955
  56. package/templates/latest/package.json +19 -21
  57. package/templates/latest/preview/index.html +125 -285
  58. package/templates/latest/public/favicon.svg +1 -0
  59. package/templates/latest/vite.config.ts +2 -8
  60. package/templates/latest/app/components/app-sidebar.tsx +0 -182
  61. package/templates/latest/app/components/chart-area-interactive.tsx +0 -290
  62. package/templates/latest/app/components/data-table.tsx +0 -807
  63. package/templates/latest/app/components/nav-documents.tsx +0 -92
  64. package/templates/latest/app/components/nav-main.tsx +0 -40
  65. package/templates/latest/app/components/nav-secondary.tsx +0 -42
  66. package/templates/latest/app/components/nav-user.tsx +0 -111
  67. package/templates/latest/app/components/section-cards.tsx +0 -102
  68. package/templates/latest/app/components/site-header.tsx +0 -28
  69. package/templates/latest/app/components/ui/avatar.tsx +0 -53
  70. package/templates/latest/app/components/ui/badge.tsx +0 -46
  71. package/templates/latest/app/components/ui/breadcrumb.tsx +0 -109
  72. package/templates/latest/app/components/ui/chart.tsx +0 -352
  73. package/templates/latest/app/components/ui/checkbox.tsx +0 -30
  74. package/templates/latest/app/components/ui/drawer.tsx +0 -139
  75. package/templates/latest/app/components/ui/select.tsx +0 -183
  76. package/templates/latest/app/components/ui/table.tsx +0 -117
  77. package/templates/latest/app/components/ui/toggle-group.tsx +0 -73
  78. package/templates/latest/app/components/ui/toggle.tsx +0 -47
  79. package/templates/latest/app/data/data.json +0 -614
  80. package/templates/latest/app/data/mock-payments.json +0 -122
  81. package/templates/latest/app/data/mock-transactions.json +0 -128
  82. package/templates/latest/app/hooks/use-bluecopa-user.ts +0 -37
  83. package/templates/latest/app/lib/utils.ts +0 -6
  84. package/templates/latest/app/routes/apitest.tsx +0 -2118
  85. package/templates/latest/app/routes/comments.tsx +0 -588
  86. package/templates/latest/app/routes/dashboard.tsx +0 -36
  87. package/templates/latest/app/routes/payments.tsx +0 -342
  88. package/templates/latest/app/routes/statements.tsx +0 -493
  89. package/templates/latest/app/routes/websocket.tsx +0 -450
  90. package/templates/latest/app/routes.tsx +0 -22
  91. package/templates/latest/dist/assets/__federation_expose_App-D-lv9y21.js +0 -161
  92. package/templates/latest/dist/assets/__federation_fn_import-CzfA7kmP.js +0 -438
  93. package/templates/latest/dist/assets/__federation_shared_react-Bp6HVBS4.js +0 -16
  94. package/templates/latest/dist/assets/__federation_shared_react-dom-BCcRGiYp.js +0 -17
  95. package/templates/latest/dist/assets/client-Dms8K6Dw.js +0 -78879
  96. package/templates/latest/dist/assets/index-BrhXrqF7.js +0 -60
  97. package/templates/latest/dist/assets/index-BzNimew1.js +0 -69
  98. package/templates/latest/dist/assets/index-DMFtQdNS.js +0 -412
  99. package/templates/latest/dist/assets/remoteEntry.css +0 -3996
  100. package/templates/latest/dist/assets/remoteEntry.js +0 -88
  101. package/templates/latest/dist/favicon.ico +0 -0
  102. package/templates/latest/dist/index.html +0 -19
@@ -1,936 +1,759 @@
1
- # AI Agent Guide for Bluecopa React UI Template
1
+ # CLAUDE.md React MFE Boilerplate
2
2
 
3
- This guide helps AI assistants and developers understand the project structure, patterns, and best practices for working with this Bluecopa React application template.
3
+ This file provides guidance to Claude Code when working with the Bluecopa React MFE (Micro-Frontend) boilerplate.
4
4
 
5
- ## 🏗️ Project Architecture Overview
5
+ ## Project Overview
6
6
 
7
- ### Core Stack
8
- - **React 18** with TypeScript
9
- - **React Router v7** with SSR support
10
- - **shadcn/ui** components built on Radix UI
11
- - **Bluecopa UI** components from `@bluecopa-ui` registry (built on Radix UI)
12
- - **Tailwind CSS v4** with CSS variables
13
- - **Vite 6** for build tooling
14
- - **TanStack Query** (via @bluecopa/react)
7
+ A production-ready React microfrontend template for building apps that run inside the Bluecopa platform. Uses single-spa for MFE orchestration, Module Federation for runtime loading, and the Dream Light design system for UI.
15
8
 
16
- ### Key Directories
17
- ```
18
- app/
19
- ├── components/ # Reusable UI components
20
- │ ├── ui/ # Bluecopa UI or shadcn/ui components
21
- │ ├── app-sidebar.tsx # Main navigation
22
- │ ├── data-table.tsx # TanStack Table wrapper
23
- │ └── chart-*.tsx # Recharts components
24
- ├── hooks/ # Custom React hooks
25
- ├── lib/ # Utilities and configurations
26
- ├── routes/ # React Router pages
27
- └── dashboard/ # Sample data and components
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ pnpm dev # Dev server on port 8080
13
+ pnpm build # TypeScript check + production build
14
+ pnpm start # Serve built MFE on port 3001
15
+ pnpm typecheck # TypeScript validation only
28
16
  ```
29
17
 
30
- ## 🎨 Component Patterns
18
+ ## Architecture
31
19
 
32
- ### UI Component Registries
20
+ ### Entry Points
33
21
 
34
- This project supports components from two registries:
22
+ | File | Mode | Router | Use Case |
23
+ |------|------|--------|----------|
24
+ | `app/main.tsx` | Standalone | `BrowserRouter` | Local development |
25
+ | `app/single-spa.tsx` | MFE | `MemoryRouter` | Deployed inside Bluecopa shell |
35
26
 
36
- 1. **Bluecopa UI Registry** (Preferred for Bluecopa-specific components)
37
- - Registry URL: https://blui.vercel.app
38
- - Registry JSON: https://blui.vercel.app/r/registry.json
39
- - Custom Bluecopa components built on Radix UI
27
+ ### MFE Props Interface
40
28
 
41
- 2. **shadcn/ui Registry** (Standard components)
42
- - Registry URL: https://ui.shadcn.com
43
- - Standard shadcn/ui components built on Radix UI
29
+ ```typescript
30
+ interface MfeProps {
31
+ apiBaseUrl?: string; // Bluecopa API URL
32
+ workspaceId?: string; // Workspace context
33
+ accessToken?: string; // JWT auth token
34
+ userId?: string; // Current user ID
35
+ basename?: string; // Router basename
36
+ }
37
+ ```
44
38
 
45
- Both registries:
46
- - Located in `app/components/ui/` after installation
47
- - Built on Radix UI primitives
48
- - Styled with Tailwind CSS
49
- - Use `cn()` utility for class merging
50
- - Support dark mode via CSS variables
39
+ Props are passed from the host app via single-spa `customProps` or `manualMount()`.
51
40
 
52
- ### Adding Components
41
+ ### Key Files
53
42
 
54
- #### From Bluecopa UI Registry
43
+ | File | Purpose |
44
+ |------|---------|
45
+ | `app/app.tsx` | Root component — QueryClient, ChartProvider, MFE config, AppProvider |
46
+ | `app/app.css` | Tailwind v4 config + Dream Light theme tokens |
47
+ | `app/contexts/app-context.tsx` | User, workspace settings, admin role context |
48
+ | `app/routes/index.tsx` | Route definitions (all wrapped in AppLayout) |
49
+ | `app/utils/utils.ts` | `cn()` utility with `copa` prefix support |
50
+ | `app/utils/component-style.ts` | Unprefixed style tokens (source of truth) |
51
+ | `app/utils/style-drivers.ts` | Auto-prefixes style tokens with `copa:` |
52
+ | `vite.config.ts` | Vite + Module Federation + Tailwind v4 |
53
+ | `components.json` | shadcn CLI config (prefix: `copa`) |
55
54
 
56
- To add a component from the Bluecopa UI registry (preferred for Bluecopa-specific components):
55
+ ### Directory Layout
57
56
 
58
- ```bash
59
- pnpm dlx shadcn@latest add @bluecopa-ui/button
60
- pnpm dlx shadcn@latest add @bluecopa-ui/card
61
- pnpm dlx shadcn@latest add @bluecopa-ui/dialog
57
+ ```
58
+ app/
59
+ ├── components/
60
+ │ ├── ui/ # shadcn/Dream Light components (prefixed)
61
+ │ ├── charts/ # ECharts-based chart components (BarChart, DonutChart, etc.)
62
+ │ └── layouts/ # AppLayout, AppSidebar, SiteHeader, NavMain, NavUser
63
+ ├── constants/ # Route paths, API defaults, query defaults, breakpoints
64
+ ├── contexts/ # AppContext (user, workspace, settings)
65
+ ├── hooks/ # use-mobile.ts (768px breakpoint)
66
+ ├── pages/ # Page components (dashboard, settings, etc.)
67
+ ├── routes/ # Route config
68
+ ├── types/ # MfeProps, AppUser, WorkspaceDataSettings
69
+ └── utils/ # cn(), style drivers
62
70
  ```
63
71
 
64
- #### From shadcn/ui Registry
65
-
66
- To add a component from the standard shadcn/ui registry:
72
+ ### Layout Structure
67
73
 
68
- ```bash
69
- pnpm dlx shadcn@latest add button
70
- pnpm dlx shadcn@latest add card
71
- pnpm dlx shadcn@latest add dialog
72
74
  ```
73
-
74
- The registries are configured in `components.json`:
75
- ```json
76
- {
77
- "registries": {
78
- "@bluecopa-ui": "https://blui.vercel.app/r/{name}.json"
79
- }
80
- }
75
+ AppLayout (SidebarProvider)
76
+ ├── AppSidebar (collapsible, Cmd/Ctrl+B toggle)
77
+ │ ├── NavMain (navigation items)
78
+ │ └── NavUser (user menu footer)
79
+ └── SidebarInset
80
+ ├── SiteHeader (breadcrumbs, user dropdown)
81
+ └── <Outlet /> (page content)
81
82
  ```
82
83
 
83
- **Note**: When no registry prefix is specified, components are added from the default shadcn/ui registry. Use `@bluecopa-ui/` prefix for Bluecopa-specific components.
84
+ ## Tailwind v4 + `copa:` Prefix CRITICAL
84
85
 
85
- ### Custom Components
86
- - Use TypeScript interfaces for props
87
- - Follow React best practices (hooks, memo, etc.)
88
- - Implement proper accessibility
89
- - Use semantic HTML elements
86
+ All Tailwind classes MUST use the `copa:` prefix for MFE CSS isolation.
90
87
 
91
- ### Example Component Structure
92
- ```tsx
93
- import { cn } from "~/lib/utils"
88
+ ### Prefix Order Rule
94
89
 
95
- interface ComponentProps {
96
- className?: string
97
- children: React.ReactNode
98
- }
90
+ **Prefix ALWAYS comes first**, then variants, then utility:
99
91
 
100
- export function Component({ className, children }: ComponentProps) {
101
- return (
102
- <div className={cn("base-styles", className)}>
103
- {children}
104
- </div>
105
- )
106
- }
92
+ ```
93
+ {prefix}:{variants}:{utility}
107
94
  ```
108
95
 
109
- ## 🔧 Development Patterns
110
-
111
- ### State Management
112
- - Use React hooks (useState, useReducer, useContext)
113
- - TanStack Query for server state
114
- - Local state for UI interactions
115
- - Avoid prop drilling with context
116
-
117
- ### Data Fetching
118
- - Use `@bluecopa/react` hooks for API calls
119
- - Implement loading and error states
120
- - Use TanStack Query for caching
121
- - Handle optimistic updates
122
-
123
- # @bluecopa/react [![npm version](https://img.shields.io/npm/v/@bluecopa/react)](https://www.npmjs.com/package/@bluecopa/react) [![License](https://img.shields.io/npm/l/@bluecopa/react)](https://github.com/bluecopa/blui/blob/main/packages/react/LICENSE)
124
-
125
- ## A Comprehensive React Query Integration for Bluecopa
126
-
127
- A React library providing opinionated custom hooks for TanStack React Query integration with Bluecopa core API. This package enables efficient data fetching, caching, and synchronization with the Bluecopa platform while maintaining type safety and developer experience.
128
-
129
- ## Table of Contents
130
-
131
- - [AI Agent Guide for Bluecopa React UI Template](#ai-agent-guide-for-bluecopa-react-ui-template)
132
- - [🏗️ Project Architecture Overview](#️-project-architecture-overview)
133
- - [Core Stack](#core-stack)
134
- - [Key Directories](#key-directories)
135
- - [🎨 Component Patterns](#-component-patterns)
136
- - [UI Component Registries](#ui-component-registries)
137
- - [Adding Components](#adding-components)
138
- - [From Bluecopa UI Registry](#from-bluecopa-ui-registry)
139
- - [From shadcn/ui Registry](#from-shadcnui-registry)
140
- - [Custom Components](#custom-components)
141
- - [Example Component Structure](#example-component-structure)
142
- - [🔧 Development Patterns](#-development-patterns)
143
- - [State Management](#state-management)
144
- - [Data Fetching](#data-fetching)
145
- - [@bluecopa/react ](#bluecopareact--)
146
- - [A Comprehensive React Query Integration for Bluecopa](#a-comprehensive-react-query-integration-for-bluecopa)
147
- - [Table of Contents](#table-of-contents)
148
- - [Features](#features)
149
- - [Installation](#installation)
150
- - [Peer Dependencies](#peer-dependencies)
151
- - [Usage](#usage)
152
- - [Query Provider Setup](#query-provider-setup)
153
- - [Boilerplate Integration](#boilerplate-integration)
154
- - [Hook Examples](#hook-examples)
155
- - [`useUser` - Fetch authenticated user](#useuser---fetch-authenticated-user)
156
- - [`useDataset` - Fetch dataset with query controls](#usedataset---fetch-dataset-with-query-controls)
157
- - [API Documentation](#api-documentation)
158
- - [`useUser(options?)`](#useuseroptions)
159
- - [`useDataset(datasetId, options?)`](#usedatasetdatasetid-options)
160
- - [`useDatasetSample(datasetId, options?)`](#usedatasetsampledatasetid-options)
161
- - [`useMetric(metricId, options?)`](#usemetricmetricid-options)
162
- - [`useInputTable(inputTableId, options?)`](#useinputtableinputtableid-options)
163
- - [`useGetFileUrlByFileId(fileId, options?)`](#usegetfileurlbyfileidfileid-options)
164
- - [`useGetPublishedWorkbookById(workbookId, options?)`](#usegetpublishedworkbookbyidworkbookid-options)
165
- - [`useGetTableById(tableId, options?)`](#usegettablebyidtableid-options)
166
- - [`useGetWorkbooksByType(workbookType, options?)`](#usegetworkbooksbytypeworkbooktype-options)
167
- - [`useGetWorkflowInstanceStatusById(instanceId, options?)`](#usegetworkflowinstancestatusbyidinstanceid-options)
168
- - [`useGetWorksheets(options?)`](#usegetworksheetsoptions)
169
- - [`useGetWorksheetsByType(worksheetType, options?)`](#usegetworksheetsbytypeworksheettype-options)
170
- - [`useRunDefinition(definitionId, options?)`](#userundefinitiondefinitionid-options)
171
- - [`useRunPublishedDefinition(publishedDefinitionId, options?)`](#userunpublisheddefinitionpublisheddefinitionid-options)
172
- - [`useRunSampleDefinition(sampleDefinitionId, options?)`](#userunsampledefinitionsampledefinitionid-options)
173
- - [`useTriggerHttpWorkflow(workflowId, payload, options?)`](#usetriggerhttpworkflowworkflowid-payload-options)
174
- - [`useTriggerWorkflow(workflowId, options?)`](#usetriggerworkflowworkflowid-options)
175
- - [`useWorkbook(workbookId, options?)`](#useworkbookworkbookid-options)
176
- - [`useWorkflow(workflowId, options?)`](#useworkflowworkflowid-options)
177
- - [`useWorksheet(worksheetId, options?)`](#useworksheetworksheetid-options)
178
- - [Configuration](#configuration)
179
- - [Default Query Configuration](#default-query-configuration)
180
- - [Customizable Parameters](#customizable-parameters)
181
- - [Advanced Usage](#advanced-usage)
182
- - [Error Handling](#error-handling)
183
- - [Manual Refetching](#manual-refetching)
184
- - [Re-exports](#re-exports)
185
- - [Styling Guidelines](#styling-guidelines)
186
- - [📊 Data Visualization](#-data-visualization)
187
- - [Charts (Recharts)](#charts-recharts)
188
- - [Tables (TanStack Table)](#tables-tanstack-table)
189
- - [🎯 Common Tasks \& Solutions](#-common-tasks--solutions)
190
- - [Adding New Pages](#adding-new-pages)
191
- - [Adding New Components](#adding-new-components)
192
- - [Working with Forms](#working-with-forms)
193
- - [API Integration](#api-integration)
194
- - [🚀 Performance Optimization](#-performance-optimization)
195
- - [Code Splitting](#code-splitting)
196
- - [Bundle Optimization](#bundle-optimization)
197
- - [Runtime Performance](#runtime-performance)
198
- - [🧪 Testing Strategies](#-testing-strategies)
199
- - [Component Testing](#component-testing)
200
- - [Integration Testing](#integration-testing)
201
- - [🔒 Security Considerations](#-security-considerations)
202
- - [Data Handling](#data-handling)
203
- - [📱 Responsive Design](#-responsive-design)
204
- - [Breakpoints](#breakpoints)
205
- - [Layout Patterns](#layout-patterns)
206
- - [🌙 Dark Mode Implementation](#-dark-mode-implementation)
207
- - [Theme System](#theme-system)
208
- - [Component Theming](#component-theming)
209
- - [🐛 Debugging Tips](#-debugging-tips)
210
- - [Common Issues](#common-issues)
211
- - [Development Tools](#development-tools)
212
- - [📚 Useful Resources](#-useful-resources)
213
- - [Documentation](#documentation)
214
- - [Bluecopa Specific](#bluecopa-specific)
215
- - [🎯 Best Practices Summary](#-best-practices-summary)
216
- - [🔄 Common Workflows](#-common-workflows)
217
- - [Adding a New Feature](#adding-a-new-feature)
218
- - [Debugging Issues](#debugging-issues)
219
-
220
- ## Features
221
-
222
- - ✅ First-class TypeScript support with strict type definitions
223
- - 🔁 Seamless integration with TanStack React Query (v5+)
224
- - 🛡 Comprehensive error handling patterns
225
- - ⚙ Optimized default query configuration
226
- - 📦 Re-exports of core TanStack React Query utilities
227
- - 📊 Sample data preview capabilities
228
- - 🧩 Customizable query parameters (limit, caching, retries)
229
-
230
- ## Installation
96
+ ```tsx
97
+ // CORRECT
98
+ className="copa:flex copa:items-center copa:hover:bg-accent copa:md:block"
99
+ className="copa:group-data-[state=open]:block"
231
100
 
232
- ```bash
233
- npm install @bluecopa/react
234
- # or with pnpm
235
- pnpm add @bluecopa/react
101
+ // WRONG — silently fails, no CSS generated, no errors
102
+ className="hover:copa:bg-accent" // variant before prefix
103
+ className="md:copa:block" // breakpoint before prefix
236
104
  ```
237
105
 
238
- ### Peer Dependencies
106
+ ### CSS Architecture (app.css)
239
107
 
240
- This package requires the following in your application:
108
+ Three layers in order:
241
109
 
242
- ```bash
243
- npm install react@^18.0.0 react-dom@^18.0.0
110
+ 1. **`@theme` (build-time)** — Resolved at compile time. Cannot use `var()` refs to runtime CSS variables. Must hardcode values.
111
+ 2. **`@theme inline` (runtime)** — Binds to CSS variables from `.mfe-root`. Used for dynamic radius, colors.
112
+ 3. **`.mfe-root` (scoped vars)** — All CSS variables scoped here, NOT `:root`. This prevents MFE style leaking.
113
+
114
+ ### cn() Utility
115
+
116
+ ```typescript
117
+ import { cn } from "~/utils/utils";
118
+
119
+ // Handles copa: prefix correctly via extendTailwindMerge({ prefix: "copa" })
120
+ <div className={cn("copa:flex copa:gap-2", isActive && "copa:bg-accent", className)} />
244
121
  ```
245
122
 
246
- ## Usage
123
+ ### Style Drivers
247
124
 
248
- ### Query Provider Setup
125
+ For reusable style tokens, define unprefixed in `component-style.ts`, consume prefixed from `style-drivers.ts`:
249
126
 
250
- Wrap your application with the React Query provider:
127
+ ```typescript
128
+ // component-style.ts (write unprefixed)
129
+ export const styles = {
130
+ card: "rounded-lg border bg-card text-card-foreground shadow-sm",
131
+ };
251
132
 
252
- ```tsx
253
- import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
254
-
255
- const queryClient = new QueryClient({
256
- defaultOptions: {
257
- queries: {
258
- staleTime: 60 * 1000, // 1 minute
259
- refetchOnWindowFocus: false,
260
- },
261
- },
262
- });
133
+ // style-drivers.ts (auto-prefixes)
134
+ // "rounded-lg border ..." "copa:rounded-lg copa:border ..."
263
135
 
264
- function App() {
265
- return (
266
- <QueryClientProvider client={queryClient}>
267
- <YourApp />
268
- <ReactQueryDevtools initialIsOpen={false} />
269
- </QueryClientProvider>
270
- );
271
- }
136
+ // Usage
137
+ import { prefixedStyles } from "~/utils/style-drivers";
138
+ <div className={prefixedStyles.card} />
272
139
  ```
273
140
 
274
- ### Boilerplate Integration
141
+ ## @bluecopa/react — API Integration
275
142
 
276
- For projects using the Bluecopa React boilerplate, use the pre-configured `QueryProvider` component that handles API configuration automatically:
143
+ React Query hooks wrapping `@bluecopa/core`. All data fetching uses these hooks.
277
144
 
278
- ```tsx
279
- // src/providers/query-provider.tsx
280
- import { ReactQueryDevtools, reactQuery, copaSetConfig } from "@bluecopa/react";
281
- import { useEffect, useState } from "react";
282
-
283
- const { QueryClient, QueryClientProvider } = reactQuery;
284
-
285
- export default function QueryProvider({ children }: { children: React.ReactNode }) {
286
- const [queryClient] = useState(() => new QueryClient({
287
- defaultOptions: {
288
- queries: {
289
- staleTime: 60 * 1000, // 1 minute
290
- refetchOnWindowFocus: false,
291
- },
292
- },
293
- }));
294
-
295
- useEffect(() => {
296
- let copaUser: any = {};
297
- try {
298
- const copaToken = import.meta.env.VITE_BLUECOPA_API_TOKEN
299
- ? atob(import.meta.env.VITE_BLUECOPA_API_TOKEN)
300
- : "{}";
301
-
302
- copaUser = JSON.parse(copaToken);
303
- } catch (error) {
304
- console.warn("Failed to parse VITE_BLUECOPA_API_TOKEN:", error);
305
- }
306
-
307
- copaSetConfig({
308
- apiBaseUrl: import.meta.env.VITE_BLUECOPA_API_URL || "https://develop.bluecopa.com/api/v1",
309
- workspaceId: import.meta.env.VITE_BLUECOPA_WORKSPACE_ID || "",
310
- accessToken: copaUser?.accessToken || "",
311
- });
312
- }, []);
145
+ ### Setup Flow
313
146
 
314
- return (
315
- <QueryClientProvider client={queryClient}>
316
- {children}
317
- <ReactQueryDevtools initialIsOpen={false} />
318
- </QueryClientProvider>
319
- );
320
- }
321
- ```
147
+ 1. `copaSetConfig()` called in `app.tsx` useEffect with MFE props or env vars
148
+ 2. `QueryClientProvider` wraps the app (staleTime: 5min, retry: 1)
149
+ 3. Hooks become available in all child components
322
150
 
323
- **Required Environment Variables:**
151
+ ### Configuration
324
152
 
325
- | Variable | Description | Example |
326
- |----------|-------------|---------|
327
- | `VITE_BLUECOPA_API_URL` | Base URL for Bluecopa API | `https://develop.bluecopa.com` |
328
- | `VITE_BLUECOPA_WORKSPACE_ID` | Your workspace identifier | `my-workspace-123` |
329
- | `VITE_BLUECOPA_API_TOKEN` | Base64-encoded JSON string containing `accessToken` | `eyJhY2Nlc3NUb2tlbiI6IjEyMzQ1In0=` |
153
+ ```typescript
154
+ import { copaSetConfig } from "@bluecopa/react";
330
155
 
331
- **Example `.env` file:**
332
- ```
333
- VITE_BLUECOPA_API_URL=https://develop.bluecopa.com
334
- VITE_BLUECOPA_WORKSPACE_ID=your-workspace-id
335
- VITE_BLUECOPA_API_TOKEN=base64-encoded-json-here
156
+ copaSetConfig({
157
+ apiBaseUrl: "https://develop.bluecopa.com/api/v1",
158
+ workspaceId: "your-workspace-id",
159
+ accessToken: "your-jwt-token",
160
+ userId: "user-id",
161
+ });
336
162
  ```
337
163
 
338
- Then wrap your application with this provider:
164
+ ### Environment Variables
339
165
 
340
- ```tsx
341
- import QueryProvider from './providers/query-provider';
166
+ ```bash
167
+ VITE_BLUECOPA_API_URL=https://develop.bluecopa.com/api/v1
168
+ VITE_BLUECOPA_WORKSPACE_ID=prod
169
+ ```
342
170
 
343
- function App() {
344
- return (
345
- <QueryProvider>
346
- <YourApp />
347
- </QueryProvider>
348
- );
349
- }
171
+ ### Available Hooks
172
+
173
+ #### User & Auth
174
+ - `useUser()` — Current user details
175
+ - `useGetAllUsers()` — All workspace users
176
+
177
+ #### Datasets & Tables
178
+ - `useDataset(id, { limit })` — Dataset records
179
+ - `useDatasetSample(id)` — Sample data
180
+ - `useRows(tableId, { filters, limit, offset, order, order_by })` — Supabase-style filtered rows
181
+ - `useInputTable(id, { limitParams, pageParams, sortParams })` — Input table data
182
+ - `useGetTableById(id)` — Table metadata
183
+ - `useInsertRow()` — Insert row (mutation, auto-invalidates)
184
+ - `useUpdateRow()` — Update row (mutation)
185
+ - `useDeleteRow()` — Delete row (mutation)
186
+
187
+ #### Metrics
188
+ - `useMetric(id)` — Metric data
189
+
190
+ #### Workbooks & Worksheets
191
+ - `useGetWorkbooksByType(type)` — Filter workbooks
192
+ - `useGetPublishedWorkbookById(id)` — Published workbook
193
+ - `useGetWorkbookDetails(id)` — Detailed info
194
+ - `useSaveWorkbook()` — Save (mutation)
195
+ - `usePublishWorkbook()` — Publish (mutation)
196
+ - `useGetWorksheets()` — All worksheets
197
+ - `useGetWorksheetsByType(type)` — Filter by type
198
+
199
+ #### Views & Runs
200
+ - `useGetViewById(id)` — View details
201
+ - `useGetViewsBySheetId(id)` — Views for sheet
202
+ - `useGetRunsByViewId(id)` — Run history
203
+ - `useGetRunResultById(id)` — Run result
204
+
205
+ #### Workflows
206
+ - `useTriggerWorkflow()` — Trigger execution (mutation)
207
+ - `useTriggerHttpWorkflow()` — HTTP trigger (mutation)
208
+ - `useGetWorkflowInstanceStatusById(id)` — Check status
209
+ - `useGetAllHttpTriggers()` — List triggers
210
+
211
+ #### Definitions
212
+ - `useRunDefinition()` — Execute definition (mutation)
213
+ - `useRunPublishedDefinition()` — Published definition (mutation)
214
+ - `useRunSampleDefinition()` — Sample definition (mutation)
215
+
216
+ #### Files
217
+ - `useFileUpload()` — Upload to S3 (mutation)
218
+ - `useFileDownload()` — Download file (mutation)
219
+ - `useGetFileUrlByFileId(id)` — Get file URL
220
+
221
+ #### Forms
222
+ - `useGetFormById(id)` — Form details
223
+ - `useGetFormSchema(id)` — Form schema
224
+ - `useGetFormData(id)` — Form data
225
+ - `useCreateOrUpdateForm()` — Create/update (mutation)
226
+
227
+ #### Chat & Comments
228
+ - `useCreateThread()` — Create thread (mutation)
229
+ - `useGetCommentsByThreadId(id)` — Thread comments
230
+ - `usePostComment()` — Post comment (mutation)
231
+ - `useUpdateComment()` / `useDeleteComment()` — Manage comments
232
+
233
+ #### Tasks & Inbox
234
+ - `useGetTaskDetails(id)` — Task details
235
+ - `useMarkTaskDone()` — Complete task (mutation)
236
+ - `useReassignTask()` — Reassign (mutation)
237
+ - `useGetAllInboxItems()` — Inbox items
238
+ - `useMarkItemAsRead()` / `useMarkItemAsUnread()` — Read state
239
+
240
+ #### Statements
241
+ - `useCreateStatementRun()` — Create run (mutation)
242
+ - `useGetStatementData(id)` — Statement data
243
+
244
+ #### Audit & Recon
245
+ - `useGetAuditLogs()` — Audit logs
246
+ - `useCreateAuditLog()` — Create entry (mutation)
247
+ - `useGetAllRecon()` — Reconciliations
248
+ - `useRunRecon()` — Run recon (mutation)
249
+
250
+ #### Pipelines
251
+ - `useGetAllTemplatedPipelines()` — Template pipelines
252
+
253
+ ### Re-exports
254
+
255
+ ```typescript
256
+ // Available from @bluecopa/react
257
+ export * as reactQuery from "@tanstack/react-query"; // QueryClient, useQuery, etc.
258
+ export * from "@bluecopa/core"; // copaSetConfig, copaApi, copaUtils
259
+ export { ReactQueryDevtools } from "@tanstack/react-query-devtools";
350
260
  ```
351
261
 
352
- This setup automatically configures the API client with your environment-specific settings and applies optimal caching defaults.
262
+ ### Hook Usage Pattern
353
263
 
354
- ### Hook Examples
264
+ ```tsx
265
+ import { useDataset } from "@bluecopa/react";
355
266
 
356
- #### `useUser` - Fetch authenticated user
267
+ function MyPage() {
268
+ const { data, isLoading, error, refetch } = useDataset("dataset-id", { limit: 100 });
357
269
 
358
- ```tsx
359
- import { useUser } from '@bluecopa/react';
360
-
361
- function UserProfile() {
362
- const { data, isLoading, error } = useUser({
363
- staleTime: 5 * 60 * 1000, // 5 minutes
364
- retry: 2
365
- });
366
-
367
- if (isLoading) return <div>Loading...</div>;
270
+ if (isLoading) return <Skeleton />;
368
271
  if (error) return <div>Error: {error.message}</div>;
369
-
370
- return <div>Welcome, {data?.name}!</div>;
371
- }
372
- ```
373
-
374
- #### `useDataset` - Fetch dataset with query controls
375
272
 
376
- ```tsx
377
- import { useDataset } from '@bluecopa/react';
378
-
379
- function DatasetViewer({ datasetId }) {
380
- const { data, isLoading } = useDataset(datasetId, {
381
- limit: 500,
382
- staleTime: 10 * 60 * 1000 // 10 minutes
383
- });
384
-
385
- if (isLoading) return <div>Loading dataset...</div>;
386
- return <div>{data?.name} ({data?.records?.length} records)</div>;
273
+ // Parse response (handles multiple formats)
274
+ const rows = data?.records || data?.data || data?.rows || (Array.isArray(data) ? data : []);
275
+ return <DataTable data={rows} />;
387
276
  }
388
277
  ```
389
278
 
390
- ## API Documentation
391
-
392
- ### `useUser(options?)`
279
+ ## @bluecopa/core — API SDK
393
280
 
394
- Fetches authenticated user details with query controls.
281
+ Low-level TypeScript SDK. Prefer `@bluecopa/react` hooks in components, use `copaApi` directly only when needed outside React.
395
282
 
396
- **Parameters:**
397
- - `options` (optional): Query options extending TanStack React Query's `UseQueryOptions`
283
+ ### Authentication
398
284
 
399
- **Returns:**
400
- - `data`: User object or `undefined`
401
- - `isLoading`: Boolean indicating loading state
402
- - `error`: Error object if request failed
403
- - `refetch`: Function to manually trigger refetch
285
+ Custom header-based auth (not Bearer tokens):
286
+ - `X-COPA-TOKEN`: Access token
287
+ - `X-COPA-WORKSPACE-ID`: Workspace context
404
288
 
405
- ### `useDataset(datasetId, options?)`
289
+ ### Direct API Usage
406
290
 
407
- Fetches dataset data by ID with configurable parameters.
291
+ ```typescript
292
+ import { copaApi } from "@bluecopa/react";
408
293
 
409
- **Parameters:**
410
- - `datasetId`: ID of the dataset to fetch
411
- - `options` (optional): Query options with:
412
- - `limit`: Maximum records to fetch
413
- - `staleTime`: Duration (ms) before data is considered stale
294
+ const user = await copaApi.user.getLoggedInUserDetails();
295
+ const rows = await copaApi.inputTable.getRows("table-id", { limit: 50 });
296
+ await copaApi.inputTable.insertRow("table-id", { name: "New", value: 42 });
297
+ ```
414
298
 
415
- **Returns:**
416
- - `data`: Dataset object containing name and records
417
- - `isLoading`: Boolean indicating loading state
418
- - `error`: Error object if request failed
419
- - `refetch`: Function to manually trigger refetch
299
+ ### WebSocket Integration
420
300
 
421
- ### `useDatasetSample(datasetId, options?)`
301
+ ```typescript
302
+ import { copaUtils } from "@bluecopa/react";
422
303
 
423
- Fetches a representative sample of dataset data.
304
+ const ws = copaUtils.websocketUtils.WebsocketContextFactory.create("centrifugo", {
305
+ connectionUrl: "wss://...",
306
+ token: "...",
307
+ userId: "...",
308
+ });
309
+ ws.connect();
310
+ ws.bind("channel", "event", (data) => console.log(data));
311
+ ```
424
312
 
425
- **Parameters:**
426
- - `datasetId`: ID of the dataset
427
- - `options` (optional): Query options with `enabled` flag
313
+ ## AppContext
314
+
315
+ Provides user data, workspace settings, and role info to all components.
316
+
317
+ ### Available Values
318
+
319
+ ```typescript
320
+ const {
321
+ user, // { userId, email, name, role, currentWorkspace, ... }
322
+ isLoading, // User fetch loading state
323
+ error, // User fetch error
324
+ refetch, // Refetch user data
325
+ isAdmin, // Derived: any team contains "admin"
326
+ workspaceIds, // User's workspace IDs
327
+ teams, // User's teams
328
+ users, // All workspace users
329
+ currentWorkspaceSettings, // { currency, timezone, dateFormat, fiscalYear, numberSettings, weekStartDay }
330
+ setCurrency, // Override currency
331
+ } = useAppContext();
332
+ ```
428
333
 
429
- **Returns:**
430
- - `data`: Object containing sample data
431
- - `isLoading`: Boolean indicating loading state
432
- - `refetch`: Function to manually trigger refetch
334
+ ### Workspace Settings Shape
433
335
 
434
- ### `useMetric(metricId, options?)`
336
+ ```typescript
337
+ {
338
+ currency: "USD",
339
+ timezone: "UTC",
340
+ dateFormat: "MM/DD/YYYY HH:mm:ss",
341
+ fiscalYear: { yearType: "calendar_year", startMonth?, startDate? },
342
+ numberSettings: { numberSystem: "intl", defaultPrecision: 2, nullPlaceholder? },
343
+ weekStartDay: "SUNDAY",
344
+ }
345
+ ```
435
346
 
436
- Fetches metric data by ID.
347
+ ## Dream Light Design System
437
348
 
438
- **Parameters:**
439
- - `metricId`: ID of the metric
440
- - `options` (optional): Query options
349
+ The UI design system. Source: `[https://blui.vercel.app/](https://blui.vercel.app/)`
350
+ Registry base URL: `https://blui.vercel.app/r/registry.json`
441
351
 
442
- **Returns:**
443
- - `data`: Metric object with name and value
444
- - `isLoading`: Boolean indicating loading state
445
- - `error`: Error object if request failed
446
- - `refetch`: Function to manually trigger refetch
352
+ ### Adding Components
447
353
 
448
- ### `useInputTable(inputTableId, options?)`
354
+ From either registry via shadcn CLI:
449
355
 
450
- Fetches input table data with limit parameters.
356
+ ```bash
357
+ # Dream Light / Bluecopa UI (preferred)
358
+ pnpm dlx shadcn@latest add @bluecopa-ui/button
359
+ pnpm dlx shadcn@latest add @bluecopa-ui/dialog
360
+ pnpm dlx shadcn@latest add @bluecopa-ui/data-table
451
361
 
452
- **Parameters:**
453
- - `inputTableId`: ID of the input table
454
- - `options` (optional): Query options with `limitParams`:
455
- - `limit`: Maximum rows to fetch
456
- - `limitFrom`: Direction to apply limit from ('top' or 'bottom')
362
+ # Standard shadcn/ui (fallback)
363
+ pnpm dlx shadcn@latest add button
364
+ ```
457
365
 
458
- **Returns:**
459
- - `data`: Input table object with rows
460
- - `isLoading`: Boolean indicating loading state
461
- - `error`: Error object if request failed
462
- - `refetch`: Function to manually trigger refetch
366
+ Registry URL: `https://blui.vercel.app/r/{name}.json`
463
367
 
464
- ### `useGetFileUrlByFileId(fileId, options?)`
368
+ ### Available Dream Light Components (70+)
465
369
 
466
- Fetches the URL for a file by its ID.
370
+ **Inputs**: button, input, textarea, checkbox, radio-group, switch, select, select-advanced, combobox, number-input, pills, date-picker, date-range-picker, calendar, toggle
467
371
 
468
- **Parameters:**
469
- - `fileId`: ID of the file to fetch URL for
470
- - `options` (optional): Query options extending TanStack React Query's `UseQueryOptions`
372
+ **Navigation**: tabs, horizontal-tabs, vertical-tabs, breadcrumb, pagination, dropdown-menu, command, sidebar, sidebar-with-icons
471
373
 
472
- **Returns:**
473
- - `data`: Object containing file URL
474
- - `isLoading`: Boolean indicating loading state
475
- - `error`: Error object if request failed
476
- - `refetch`: Function to manually trigger refetch
374
+ **Display**: text, badge, alert, avatar, toast, tooltip, spinner, loading-state, skeleton, empty-state, error-boundary, progress, kpi-card, ag-grid-table, data-table
477
375
 
478
- ### `useGetPublishedWorkbookById(workbookId, options?)`
376
+ **Containers**: card, section-card, dialog, sheet, side-panel, popover, accordion, scroll-area, separator, stack, kanban, tree-menu, topbar
479
377
 
480
- Fetches published workbook details by ID.
378
+ **Layout**: scaffold, page-container, section, page-header
481
379
 
482
- **Parameters:**
483
- - `workbookId`: ID of the published workbook
484
- - `options` (optional): Query options
380
+ **Forms**: form-field, form-section, form-error-summary, label, search-box
485
381
 
486
- **Returns:**
487
- - `data`: Published workbook object
488
- - `isLoading`: Boolean indicating loading state
489
- - `error`: Error object if request failed
490
- - `refetch`: Function to manually trigger refetch
382
+ **Advanced**: filter-builder, stepper, brand-logo, listing-view, listing-table, listing-header
491
383
 
492
- ### `useGetTableById(tableId, options?)`
384
+ ### Design Tokens
493
385
 
494
- Fetches table metadata by ID.
386
+ #### Colors (OKLCH)
495
387
 
496
- **Parameters:**
497
- - `tableId`: ID of the table
498
- - `options` (optional): Query options
388
+ ```css
389
+ --primary: #3548ff; /* Bluecopa blue */
390
+ --primary-foreground: oklch(100% 0 0); /* White on primary */
391
+ --background: oklch(0.975 0.005 280); /* Near-white */
392
+ --foreground: oklch(0.145 0 0); /* Near-black */
393
+ --destructive: oklch(0.577 0.245 27.325); /* Red */
394
+ --border: oklch(0.92 0.005 280); /* Light gray */
395
+ --muted: oklch(0.97 0.003 280);
396
+ --muted-foreground: oklch(0.556 0 0);
397
+ --accent: oklch(0.97 0.003 280);
499
398
 
500
- **Returns:**
501
- - `data`: Table metadata object
502
- - `isLoading`: Boolean indicating loading state
503
- - `error`: Error object if request failed
504
- - `refetch`: Function to manually trigger refetch
399
+ /* Semantic status */
400
+ --status-success: oklch(0.72 0.19 142);
401
+ --status-warning: oklch(0.80 0.18 85);
402
+ --status-error: oklch(0.63 0.24 25);
505
403
 
506
- ### `useGetWorkbooksByType(workbookType, options?)`
404
+ /* Sidebar */
405
+ --sidebar: oklch(1 0 0); /* White */
406
+ --sidebar-foreground: oklch(0.145 0 0);
407
+ --sidebar-border: oklch(0.92 0.004 280);
408
+ ```
507
409
 
508
- Fetches workbooks filtered by type.
410
+ #### Typography Scale
411
+
412
+ ```css
413
+ /* Fluid type scale */
414
+ --text-xs: 0.702rem; /* Small captions */
415
+ --text-sm: 0.835rem; /* Body text, labels */
416
+ --text-md: 1.004rem; /* Prominent body */
417
+ --text-lg: 1.21rem; /* Subheadings */
418
+ --text-xl: 1.452rem; /* Section headers */
419
+ --text-2xl: 1.742rem; /* Large headings */
420
+ --text-3xl: 2.093rem;
421
+ --text-4xl: 2.505rem;
422
+ --text-5xl: 3.013rem; /* Hero display */
423
+
424
+ /* Heading tokens */
425
+ --text-h1: 3rem; /* 700 weight */
426
+ --text-h2: 2.25rem; /* 700 */
427
+ --text-h3: 1.875rem; /* 600 */
428
+ --text-h4: 1.5rem; /* 400 */
429
+ --text-h5: 1.25rem; /* 600 */
430
+ --text-h6: 1rem; /* 400 */
431
+
432
+ /* Font: Satoshi (sans-serif) */
433
+ ```
509
434
 
510
- **Parameters:**
511
- - `workbookType`: Type of workbooks to fetch
512
- - `options` (optional): Query options
435
+ #### Custom Typography Utilities (defined in app.css)
513
436
 
514
- **Returns:**
515
- - `data`: Array of workbook objects
516
- - `isLoading`: Boolean indicating loading state
517
- - `error`: Error object if request failed
518
- - `refetch`: Function to manually trigger refetch
437
+ ```
438
+ .text-page-title → 1.5rem, semibold
439
+ .text-section-title → 0.9375rem, semibold
440
+ .text-label → 0.75rem, uppercase, tracked
441
+ .text-value → 1.75rem, bold
442
+ .text-nav-section → 0.6875rem, uppercase, tracked
443
+ ```
519
444
 
520
- ### `useGetWorkflowInstanceStatusById(instanceId, options?)`
445
+ #### Spacing
521
446
 
522
- Fetches workflow instance status by ID.
447
+ ```css
448
+ --radius: 1rem;
449
+ --card-radius: 1.25rem;
450
+ --rounded-input: 12px;
523
451
 
524
- **Parameters:**
525
- - `instanceId`: ID of the workflow instance
526
- - `options` (optional): Query options
452
+ /* Motion */
453
+ --duration-fast: 100ms;
454
+ --duration-normal: 200ms;
455
+ --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
456
+ ```
527
457
 
528
- **Returns:**
529
- - `data`: Workflow status object
530
- - `isLoading`: Boolean indicating loading state
531
- - `error`: Error object if request failed
532
- - `refetch`: Function to manually trigger refetch
458
+ #### Custom Shadow Utilities
533
459
 
534
- ### `useGetWorksheets(options?)`
460
+ ```
461
+ .shadow-soft-xs → Subtle card shadow
462
+ .shadow-card → Multi-layer card shadow
463
+ .shadow-card-hover → Elevated hover state
464
+ ```
535
465
 
536
- Fetches all available worksheets.
466
+ ### Chart Colors
537
467
 
538
- **Parameters:**
539
- - `options` (optional): Query options
468
+ ```css
469
+ --chart-1: oklch(0.55 0.15 255); /* Blue */
470
+ --chart-2: oklch(0.62 0.12 175); /* Teal */
471
+ --chart-3: oklch(0.70 0.13 80); /* Green */
472
+ --chart-4: oklch(0.55 0.14 295); /* Purple */
473
+ --chart-5: oklch(0.62 0.12 15); /* Red */
474
+ ```
540
475
 
541
- **Returns:**
542
- - `data`: Array of worksheet objects
543
- - `isLoading`: Boolean indicating loading state
544
- - `error`: Error object if request failed
545
- - `refetch`: Function to manually trigger refetch
476
+ ## Build System
546
477
 
547
- ### `useGetWorksheetsByType(worksheetType, options?)`
478
+ ### Vite + Module Federation
548
479
 
549
- Fetches worksheets filtered by type.
480
+ ```
481
+ Build Output:
482
+ dist/assets/remoteEntry.js ← Module Federation entry (SystemJS format)
483
+ dist/assets/remoteEntry.css ← Single bundled CSS file
484
+ ```
550
485
 
551
- **Parameters:**
552
- - `worksheetType`: Type of worksheets to fetch
553
- - `options` (optional): Query options
486
+ - **Federation name**: `__copa_ext_app__react_route`
487
+ - **Exposed module**: `./App` `./app/single-spa.tsx`
488
+ - **Shared**: `react`, `react-dom` (externalized in build)
489
+ - **Format**: SystemJS (for single-spa compatibility)
490
+ - **CSS**: Single file, no code splitting
554
491
 
555
- **Returns:**
556
- - `data`: Array of worksheet objects
557
- - `isLoading`: Boolean indicating loading state
558
- - `error`: Error object if request failed
559
- - `refetch`: Function to manually trigger refetch
492
+ ### TypeScript Config
560
493
 
561
- ### `useRunDefinition(definitionId, options?)`
494
+ - Path alias: `~/` → `./app/` (via `tsconfig.json` paths + `vite-tsconfig-paths`)
495
+ - Target: ES2020, JSX: react-jsx, strict mode
562
496
 
563
- Executes a run definition.
497
+ ### Docker
564
498
 
565
- **Parameters:**
566
- - `definitionId`: ID of the run definition
567
- - `options` (optional): Query options
499
+ ```bash
500
+ docker build -t bluecopa-mfe .
501
+ # Serves dist/ via lightweight HTTP server
502
+ ```
568
503
 
569
- **Returns:**
570
- - `data`: Execution result
571
- - `isLoading`: Boolean indicating loading state
572
- - `error`: Error object if request failed
573
- - `refetch`: Function to manually trigger refetch
504
+ ## MFE Integration
574
505
 
575
- ### `useRunPublishedDefinition(publishedDefinitionId, options?)`
506
+ ### How the Host App Loads This MFE
576
507
 
577
- Executes a published run definition.
508
+ ```javascript
509
+ // 1. Load Module Federation entry
510
+ System.import("__copa_ext_app__react_route/App");
578
511
 
579
- **Parameters:**
580
- - `publishedDefinitionId`: ID of the published definition
581
- - `options` (optional): Query options
512
+ // 2. Register with single-spa
513
+ registerApplication({
514
+ name: "bluecopa-preview",
515
+ app: () => System.import("__copa_ext_app__react_route/App"),
516
+ activeWhen: ["/app/external"],
517
+ customProps: {
518
+ apiBaseUrl: "https://develop.bluecopa.com/api/v1",
519
+ accessToken: "eyJ...",
520
+ workspaceId: "prod",
521
+ userId: "user-123",
522
+ },
523
+ });
524
+ ```
582
525
 
583
- **Returns:**
584
- - `data`: Execution result
585
- - `isLoading`: Boolean indicating loading state
586
- - `error`: Error object if request failed
587
- - `refetch`: Function to manually trigger refetch
526
+ ### Single-spa Lifecycle
588
527
 
589
- ### `useRunSampleDefinition(sampleDefinitionId, options?)`
528
+ - **bootstrap**: No-op
529
+ - **mount**: Creates React root in `#single-spa-application:bluecopa-preview`, renders `<MemoryRouter><App {...props} /></MemoryRouter>`
530
+ - **unmount**: Calls `root.unmount()`
531
+ - **errorBoundary**: Catches errors, renders fallback UI
590
532
 
591
- Executes a sample run definition.
533
+ ### Manual Mount API (for non-single-spa hosts)
592
534
 
593
- **Parameters:**
594
- - `sampleDefinitionId`: ID of the sample definition
595
- - `options` (optional): Query options
535
+ ```typescript
536
+ import { manualMount, manualUnmount } from "./single-spa";
596
537
 
597
- **Returns:**
598
- - `data`: Sample execution result
599
- - `isLoading`: Boolean indicating loading state
600
- - `error`: Error object if request failed
601
- - `refetch`: Function to manually trigger refetch
538
+ await manualMount({
539
+ domElement: document.getElementById("mfe-container"),
540
+ apiBaseUrl: "https://...",
541
+ accessToken: "...",
542
+ workspaceId: "...",
543
+ userId: "...",
544
+ basename: "/app",
545
+ });
546
+ ```
602
547
 
603
- ### `useTriggerHttpWorkflow(workflowId, payload, options?)`
548
+ ## Charts (ECharts)
604
549
 
605
- Triggers an HTTP workflow execution.
550
+ Chart components live in `app/components/charts/` and are powered by Apache ECharts via `echarts-for-react`. The `<ChartProvider>` in `app.tsx` bridges CSS design tokens to the ECharts theme.
606
551
 
607
- **Parameters:**
608
- - `workflowId`: ID of the workflow
609
- - `payload`: Request payload
610
- - `options` (optional): Query options
552
+ ### Architecture
611
553
 
612
- **Returns:**
613
- - `data`: Workflow execution response
614
- - `isLoading`: Boolean indicating loading state
615
- - `error`: Error object if request failed
616
- - `refetch`: Function to manually trigger refetch
554
+ | File | Purpose |
555
+ |------|---------|
556
+ | `chart-provider.tsx` | React context — registers ECharts theme from CSS tokens, auto-refreshes on theme change |
557
+ | `chart-theme.ts` | Reads `--chart-*` vars from `.mfe-root`, converts OKLCH to hex, builds ECharts theme object |
558
+ | `chart-utils.ts` | Composable option builders: `chartTooltip()`, `chartLegend()`, `chartGrid()`, `chartCategoryAxis()`, `chartValueAxis()`, `chartTitle()`, `chartAnimation()`, `chartColors()`, `formatChartValue()`, `formatChartCurrency()` |
559
+ | `base-chart.tsx` | Thin wrapper around `echarts-for-react` with loading/empty states, accessibility, responsive resize |
560
+ | `bar-chart.tsx` | Configurable bar chart (vertical/horizontal, stacked, click events, value labels) |
561
+ | `donut-chart.tsx` | Donut/pie chart with center text, legend, slice interactions |
562
+ | `index.ts` | Barrel exports |
617
563
 
618
- ### `useTriggerWorkflow(workflowId, options?)`
564
+ ### Available Chart Components
619
565
 
620
- Triggers a workflow execution.
566
+ #### BarChart
621
567
 
622
- **Parameters:**
623
- - `workflowId`: ID of the workflow
624
- - `options` (optional): Query options
568
+ ```tsx
569
+ import { BarChart } from "~/components/charts";
570
+
571
+ <BarChart
572
+ data={{
573
+ categories: ["Jan", "Feb", "Mar"],
574
+ series: [{ name: "Revenue", data: [4200, 3800, 5100] }],
575
+ }}
576
+ height={280}
577
+ horizontal={false} // swap to horizontal bars
578
+ stacked={false} // stack multiple series
579
+ showValues={false} // show value labels on bars
580
+ showLegend={true} // auto-shows if >1 series
581
+ formatValue={(v) => `$${(v / 1000).toFixed(1)}K`}
582
+ onBarClick={({ name, seriesName, value }) => console.log(name, value)}
583
+ loading={false}
584
+ empty={false}
585
+ />
586
+ ```
625
587
 
626
- **Returns:**
627
- - `data`: Workflow execution response
628
- - `isLoading`: Boolean indicating loading state
629
- - `error`: Error object if request failed
630
- - `refetch`: Function to manually trigger refetch
588
+ #### DonutChart
631
589
 
632
- ### `useWorkbook(workbookId, options?)`
590
+ ```tsx
591
+ import { DonutChart } from "~/components/charts";
592
+
593
+ <DonutChart
594
+ data={{
595
+ items: [
596
+ { name: "Payroll", value: 42 },
597
+ { name: "Operations", value: 28 },
598
+ { name: "Marketing", value: 16 },
599
+ ],
600
+ }}
601
+ height={280}
602
+ centerText="100%" // large text in donut center
603
+ centerSubText="Total" // smaller text below center
604
+ thickness={60} // donut ring thickness (%)
605
+ showLegend={true}
606
+ showLabels={false} // show labels on slices
607
+ onSliceClick={(params) => console.log(params)}
608
+ />
609
+ ```
633
610
 
634
- Fetches workbook details by ID.
611
+ #### BaseChart (escape hatch)
635
612
 
636
- **Parameters:**
637
- - `workbookId`: ID of the workbook
638
- - `options` (optional): Query options
613
+ For custom ECharts configurations not covered by BarChart/DonutChart:
639
614
 
640
- **Returns:**
641
- - `data`: Workbook object
642
- - `isLoading`: Boolean indicating loading state
643
- - `error`: Error object if request failed
644
- - `refetch`: Function to manually trigger refetch
615
+ ```tsx
616
+ import { BaseChart } from "~/components/charts";
617
+ import { chartTooltip, chartGrid, chartCategoryAxis, chartValueAxis } from "~/components/charts/chart-utils";
618
+
619
+ <BaseChart
620
+ option={{
621
+ ...chartTooltip(),
622
+ ...chartGrid(),
623
+ xAxis: chartCategoryAxis(["A", "B", "C"]),
624
+ yAxis: chartValueAxis(),
625
+ series: [{ type: "line", data: [10, 20, 30], smooth: true }],
626
+ }}
627
+ height={300}
628
+ loading={false}
629
+ ariaLabel="Custom line chart"
630
+ />
631
+ ```
645
632
 
646
- ### `useWorkflow(workflowId, options?)`
633
+ ### Chart Colors
647
634
 
648
- Fetches workflow configuration by ID.
635
+ Defined as `--chart-1` through `--chart-5` in `.mfe-root` (app.css). Hardcoded hex fallbacks in `chart-theme.ts` ensure colors render even if CSS vars aren't resolved:
649
636
 
650
- **Parameters:**
651
- - `workflowId`: ID of the workflow
652
- - `options` (optional): Query options
637
+ | Token | OKLCH | Hex Fallback | Color |
638
+ |-------|-------|-------------|-------|
639
+ | `--chart-1` | `oklch(0.55 0.15 255)` | `#2563eb` | Blue |
640
+ | `--chart-2` | `oklch(0.62 0.12 175)` | `#0d9488` | Teal |
641
+ | `--chart-3` | `oklch(0.70 0.13 80)` | `#ca8a04` | Gold |
642
+ | `--chart-4` | `oklch(0.55 0.14 295)` | `#7c3aed` | Purple |
643
+ | `--chart-5` | `oklch(0.62 0.12 15)` | `#dc2626` | Red |
653
644
 
654
- **Returns:**
655
- - `data`: Workflow configuration object
656
- - `isLoading`: Boolean indicating loading state
657
- - `error`: Error object if request failed
658
- - `refetch`: Function to manually trigger refetch
645
+ The theme auto-extends to 10 colors via tints/shades of the base 5.
659
646
 
660
- ### `useWorksheet(worksheetId, options?)`
647
+ ### Adding New Chart Types
661
648
 
662
- Fetches worksheet details by ID.
649
+ To add a new chart type (e.g., LineChart, PieChart), follow the pattern in `bar-chart.tsx`:
663
650
 
664
- **Parameters:**
665
- - `worksheetId`: ID of the worksheet
666
- - `options` (optional): Query options
651
+ 1. Create `app/components/charts/my-chart.tsx`
652
+ 2. Use `useMemo` to build an ECharts option from `chart-utils` helpers
653
+ 3. Render via `<BaseChart option={option} ... />`
654
+ 4. Export from `index.ts`
667
655
 
668
- **Returns:**
669
- - `data`: Worksheet object
670
- - `isLoading`: Boolean indicating loading state
671
- - `error`: Error object if request failed
672
- - `refetch`: Function to manually trigger refetch
656
+ Source chart components from the Dream Light registry: `/Users/mahipat/projects/bluecopa/shadcn-ui-registry/src/components/charts/`
673
657
 
658
+ ## Toast Notifications
674
659
 
675
- ## Configuration
660
+ Uses [Sonner](https://sonner.emilkowal.dev/) for toast notifications. The `<Toaster />` component is rendered in `app.tsx`.
676
661
 
677
- ### Default Query Configuration
662
+ ### Usage
678
663
 
679
664
  ```tsx
680
- const queryClient = new QueryClient({
681
- defaultOptions: {
682
- queries: {
683
- retry: 3,
684
- staleTime: 1000 * 60 * 5, // 5 minutes
685
- gcTime: 1000 * 60 * 10, // 10 minutes
686
- },
687
- },
688
- });
689
- ```
665
+ import { toast } from "sonner";
690
666
 
691
- ### Customizable Parameters
667
+ // Success
668
+ toast.success("Record saved", { description: "Changes applied." });
692
669
 
693
- All hooks accept standard TanStack React Query options:
670
+ // Error
671
+ toast.error("Failed to save", { description: "Check your connection." });
694
672
 
695
- ```ts
696
- interface QueryOptions {
697
- enabled?: boolean; // Enable/disable query
698
- staleTime?: number; // Duration (ms) before data is stale
699
- gcTime?: number; // Duration (ms) to keep data in cache
700
- retry?: number | boolean; // Number of retries or disable retries
701
- onSuccess?: (data: any) => void; // Success callback
702
- onError?: (error: Error) => void; // Error callback
703
- }
704
- ```
673
+ // Warning
674
+ toast.warning("Unsaved changes");
705
675
 
706
- ## Advanced Usage
676
+ // Info
677
+ toast.info("New data available");
707
678
 
708
- ### Error Handling
679
+ // With action
680
+ toast("Item deleted", {
681
+ action: { label: "Undo", onClick: () => undoDelete() },
682
+ });
709
683
 
710
- ```tsx
711
- useUser({
712
- onError: (error) => {
713
- console.error('User fetch failed:', error.message);
714
- // Custom error recovery logic
715
- }
684
+ // Promise (loading → success/error)
685
+ toast.promise(saveData(), {
686
+ loading: "Saving...",
687
+ success: "Saved!",
688
+ error: "Failed to save",
716
689
  });
717
690
  ```
718
691
 
719
- ### Manual Refetching
692
+ ### Toaster Config
693
+
694
+ The `<Toaster />` component from `~/components/ui/sonner` is pre-configured with Dream Light theme tokens. It renders in the app root (`app.tsx`) and is available globally — no additional setup needed.
695
+
696
+ ## Coding Conventions
697
+
698
+ ### Component Pattern
720
699
 
721
700
  ```tsx
722
- function ManualRefetch() {
723
- const { data, refetch } = useUser();
724
-
701
+ import { cn } from "~/utils/utils";
702
+
703
+ interface MyComponentProps {
704
+ className?: string;
705
+ children: React.ReactNode;
706
+ }
707
+
708
+ export function MyComponent({ className, children }: MyComponentProps) {
725
709
  return (
726
- <div>
727
- <button onClick={() => refetch()}>Refresh</button>
710
+ <div className={cn("copa:flex copa:items-center copa:gap-2", className)}>
711
+ {children}
728
712
  </div>
729
713
  );
730
714
  }
731
715
  ```
732
716
 
733
- ## Re-exports
734
-
735
- This package re-exports core TanStack React Query utilities:
736
-
737
- ```ts
738
- import {
739
- useQuery,
740
- useMutation,
741
- QueryClient,
742
- QueryClientProvider,
743
- ReactQueryDevtools
744
- } from '@bluecopa/react';
745
- ```
746
-
747
- ### Styling Guidelines
748
- - Use Tailwind CSS classes
749
- - Leverage CSS variables for theming
750
- - Follow mobile-first responsive design
751
- - Use `cn()` utility for conditional classes
752
-
753
- ## 📊 Data Visualization
754
-
755
- ### Charts (Recharts)
756
- - Interactive charts with hover states
757
- - Responsive design
758
- - Customizable colors via CSS variables
759
- - Accessible with proper ARIA labels
760
-
761
- ### Tables (TanStack Table)
762
- - Sortable columns
763
- - Filtering capabilities
764
- - Pagination support
765
- - Row selection
766
- - Custom cell renderers
767
-
768
- ## 🎯 Common Tasks & Solutions
769
-
770
- ### Adding New Pages
771
- 1. Create route file in `app/routes/`
772
- 2. Export default component
773
- 3. Add navigation link in sidebar
774
- 4. Implement proper TypeScript types
775
-
776
- ### Adding New Components
777
- 1. Add components from either registry:
778
- - Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/component-name`
779
- - shadcn/ui: `pnpm dlx shadcn@latest add component-name`
780
- 2. Components are installed in `app/components/ui/`
781
- 3. Prefer Bluecopa UI registry for Bluecopa-specific components
782
- 4. Use shadcn/ui registry for standard components not available in Bluecopa UI
783
- 5. Define TypeScript interface for props if extending components
784
- 6. Implement responsive design
785
- 7. Add proper accessibility attributes
786
-
787
- ### Working with Forms
788
- 1. Use form components from either registry:
789
- - Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/form`
790
- - shadcn/ui: `pnpm dlx shadcn@latest add form`
791
- 2. Prefer Bluecopa UI form components when available
792
- 3. Implement validation with Zod
793
- 4. Handle form state with React hooks
794
- 5. Show loading and error states
795
- 6. Use proper form semantics
796
-
797
- ### API Integration
798
- 1. Use `@bluecopa/react` hooks
799
- 2. Handle loading states
800
- 3. Implement error boundaries
801
- 4. Use TanStack Query for caching
802
- 5. Add optimistic updates where appropriate
803
-
804
- ## 🚀 Performance Optimization
805
-
806
- ### Code Splitting
807
- - Use React.lazy() for route-based splitting
808
- - Implement proper loading states
809
- - Avoid unnecessary re-renders
810
-
811
- ### Bundle Optimization
812
- - Use Vite's built-in optimizations
813
- - Implement proper tree shaking
814
- - Optimize images and assets
815
-
816
- ### Runtime Performance
817
- - Use React.memo() for expensive components
818
- - Implement proper key props
819
- - Avoid inline object/function creation
820
-
821
- ## 🧪 Testing Strategies
822
-
823
- ### Component Testing
824
- - Test component behavior, not implementation
825
- - Use React Testing Library
826
- - Mock external dependencies
827
- - Test accessibility features
828
-
829
- ### Integration Testing
830
- - Test user workflows
831
- - Mock API responses
832
- - Test error scenarios
833
- - Verify responsive behavior
834
-
835
- ## 🔒 Security Considerations
836
-
837
- ### Data Handling
838
- - Sanitize user inputs
839
- - Validate data on client and server
840
- - Use proper TypeScript types
841
- - Implement proper error handling
842
-
843
- ## 📱 Responsive Design
844
-
845
- ### Breakpoints
846
- - Mobile-first approach
847
- - Use Tailwind's responsive prefixes
848
- - Test on multiple devices
849
- - Implement touch-friendly interactions
850
-
851
- ### Layout Patterns
852
- - Use CSS Grid and Flexbox
853
- - Implement proper spacing
854
- - Handle overflow scenarios
855
- - Ensure proper scrolling
856
-
857
- ## 🌙 Dark Mode Implementation
858
-
859
- ### Theme System
860
- - Use next-themes for theme switching
861
- - Implement CSS variables for colors
862
- - Test both light and dark modes
863
- - Ensure proper contrast ratios
864
-
865
- ### Component Theming
866
- - Use semantic color tokens
867
- - Implement proper hover states
868
- - Test theme transitions
869
- - Ensure accessibility compliance
870
-
871
- ## 🐛 Debugging Tips
872
-
873
- ### Common Issues
874
- - Check TypeScript errors first
875
- - Verify component imports
876
- - Check Tailwind class names
877
- - Validate API responses
878
-
879
- ### Development Tools
880
- - Use React DevTools
881
- - Leverage Vite's HMR
882
- - Check browser console
883
- - Use TypeScript strict mode
884
-
885
- ## 📚 Useful Resources
886
-
887
- ### Documentation
888
- - [React Router v7 Docs](https://reactrouter.com/)
889
- - [Bluecopa UI Registry](https://blui.vercel.app)
890
- - [shadcn/ui Components](https://ui.shadcn.com/)
891
- - [Tailwind CSS v4](https://tailwindcss.com/)
892
- - [TanStack Table](https://tanstack.com/table)
893
- - [Recharts](https://recharts.org/)
894
-
895
- ### Bluecopa Specific
896
- - [@bluecopa/react Package](packages/react:1)
897
- - Bluecopa Design System
898
- - API Integration Patterns
899
-
900
- ## 🎯 Best Practices Summary
901
-
902
- 1. **Always use TypeScript** - Define proper interfaces and types
903
- 2. **Use appropriate registry** - Prefer Bluecopa UI (`@bluecopa-ui/component-name`) for Bluecopa-specific components, use shadcn/ui (`component-name`) for standard components
904
- 3. **Follow component patterns** - Use established component structure from both registries
905
- 4. **Implement accessibility** - Use semantic HTML and ARIA attributes
906
- 5. **Optimize for performance** - Use proper React patterns and code splitting
907
- 6. **Test thoroughly** - Write tests for components and user workflows
908
- 7. **Handle errors gracefully** - Implement proper error boundaries and fallbacks
909
- 8. **Follow responsive design** - Mobile-first approach with proper breakpoints
910
- 9. **Use proper state management** - Choose appropriate patterns for different data types
911
- 10. **Implement proper loading states** - Show feedback during async operations
912
- 11. **Follow security best practices** - Validate inputs and secure API calls
913
-
914
- ## 🔄 Common Workflows
915
-
916
- ### Adding a New Feature
917
- 1. Create feature branch
918
- 2. Add required UI components from appropriate registry:
919
- - Bluecopa UI: `pnpm dlx shadcn@latest add @bluecopa-ui/component-name`
920
- - shadcn/ui: `pnpm dlx shadcn@latest add component-name`
921
- 3. Implement components with TypeScript
922
- 4. Add proper styling with Tailwind
923
- 5. Implement responsive design
924
- 6. Add tests
925
- 7. Update documentation
926
- 8. Test in both light and dark modes
927
-
928
- ### Debugging Issues
929
- 1. Check browser console for errors
930
- 2. Verify TypeScript compilation
931
- 3. Test component isolation
932
- 4. Check API responses
933
- 5. Verify responsive behavior
934
- 6. Test accessibility features
935
-
936
- This guide should help AI assistants and developers work effectively with this Bluecopa React template, ensuring consistent patterns and best practices are followed throughout development.
717
+ ### Adding a New Page
718
+
719
+ 1. Create page in `app/pages/my-page.tsx`
720
+ 2. Add route in `app/routes/index.tsx` inside the `<Route element={<AppLayout />}>` group
721
+ 3. Add navigation item in `app/components/layouts/app-sidebar.tsx`
722
+
723
+ ### Adding a New UI Component
724
+
725
+ ```bash
726
+ # Preferred: Dream Light registry
727
+ pnpm dlx shadcn@latest add @bluecopa-ui/component-name
728
+
729
+ # Fallback: standard shadcn
730
+ pnpm dlx shadcn@latest add component-name
731
+ ```
732
+
733
+ Components install to `app/components/ui/` with `copa:` prefix pre-configured.
734
+
735
+ ### Icons
736
+
737
+ Use `lucide-react` or `@tabler/icons-react` (both available).
738
+
739
+ ### State Management
740
+
741
+ - **Server state**: `@bluecopa/react` hooks (React Query)
742
+ - **App context**: `useAppContext()` for user/workspace/settings
743
+ - **Local state**: `useState` / `useReducer`
744
+ - **No global state library** — keep it simple
745
+
746
+ ### Responsive Design
747
+
748
+ - Mobile breakpoint: 768px (`useIsMobile()` hook)
749
+ - Sidebar: Desktop = collapsible panel, Mobile = sheet overlay
750
+ - Use `copa:md:` prefix for desktop-only styles
751
+
752
+ ### Important Gotchas
753
+
754
+ 1. **Prefix order**: `copa:` MUST come before any variant. `copa:hover:bg-accent` not `hover:copa:bg-accent`
755
+ 2. **`@theme` is build-time**: Cannot reference `.mfe-root` CSS variables in `@theme` blocks. Use `@theme inline` for runtime bindings.
756
+ 3. **`.mfe-root` not `:root`**: All CSS variables scoped to `.mfe-root` for MFE isolation
757
+ 4. **Breakpoints with prefix**: Must explicitly define `--breakpoint-*` vars in `@theme` (Tailwind v4 doesn't auto-include them with prefix)
758
+ 5. **ReactQueryDevtools**: Only rendered in dev mode (`import.meta.env.DEV`)
759
+ 6. **Config before queries**: `copaSetConfig()` must complete before React Query hooks fire. The app gates rendering behind `isConfigured` state.