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.
- package/README.md +7 -5
- package/package.json +1 -1
- package/templates/latest/.claude/settings.local.json +56 -0
- package/templates/latest/.env.example +8 -0
- package/templates/latest/Agent.md +598 -775
- package/templates/latest/CLAUDE.md +759 -0
- package/templates/latest/README.md +11 -2
- package/templates/latest/app/app.css +292 -85
- package/templates/latest/app/app.tsx +48 -39
- package/templates/latest/app/components/bluecopa-logo.tsx +20 -0
- package/templates/latest/app/components/charts/bar-chart.tsx +132 -0
- package/templates/latest/app/components/charts/base-chart.tsx +149 -0
- package/templates/latest/app/components/charts/chart-provider.tsx +71 -0
- package/templates/latest/app/components/charts/chart-theme.ts +262 -0
- package/templates/latest/app/components/charts/chart-utils.ts +142 -0
- package/templates/latest/app/components/charts/donut-chart.tsx +110 -0
- package/templates/latest/app/components/charts/index.ts +5 -0
- package/templates/latest/app/components/layouts/app-layout.tsx +22 -0
- package/templates/latest/app/components/layouts/app-sidebar.tsx +88 -0
- package/templates/latest/app/components/layouts/nav-main.tsx +50 -0
- package/templates/latest/app/components/layouts/nav-user.tsx +38 -0
- package/templates/latest/app/components/layouts/site-header.tsx +93 -0
- package/templates/latest/app/components/loading-screen.tsx +41 -0
- package/templates/latest/app/components/ui/ag-grid-table.tsx +79 -0
- package/templates/latest/app/components/ui/button.tsx +23 -23
- package/templates/latest/app/components/ui/card.tsx +20 -20
- package/templates/latest/app/components/ui/dropdown-menu.tsx +54 -49
- package/templates/latest/app/components/ui/input.tsx +8 -8
- package/templates/latest/app/components/ui/label.tsx +8 -8
- package/templates/latest/app/components/ui/separator.tsx +7 -7
- package/templates/latest/app/components/ui/sheet.tsx +43 -32
- package/templates/latest/app/components/ui/sidebar.tsx +240 -235
- package/templates/latest/app/components/ui/skeleton.tsx +4 -4
- package/templates/latest/app/components/ui/sonner.tsx +6 -9
- package/templates/latest/app/components/ui/tabs.tsx +15 -15
- package/templates/latest/app/components/ui/tooltip.tsx +18 -12
- package/templates/latest/app/constants/index.ts +31 -0
- package/templates/latest/app/contexts/app-context.tsx +201 -0
- package/templates/latest/app/hooks/use-mobile.ts +13 -12
- package/templates/latest/app/main.tsx +1 -1
- package/templates/latest/app/pages/dashboard.tsx +246 -0
- package/templates/latest/app/pages/payments.tsx +182 -0
- package/templates/latest/app/pages/settings.tsx +128 -0
- package/templates/latest/app/routes/index.tsx +19 -0
- package/templates/latest/app/single-spa.tsx +69 -186
- package/templates/latest/app/types/index.ts +37 -0
- package/templates/latest/app/utils/ag-grid-datasource.ts +63 -0
- package/templates/latest/app/utils/ag-grid-license.ts +12 -0
- package/templates/latest/app/utils/ag-grid-theme.ts +9 -0
- package/templates/latest/app/utils/component-style.ts +7 -0
- package/templates/latest/app/utils/style-drivers.ts +24 -0
- package/templates/latest/app/utils/utils.ts +10 -0
- package/templates/latest/components.json +3 -3
- package/templates/latest/index.html +30 -2
- package/templates/latest/package-lock.json +2717 -955
- package/templates/latest/package.json +19 -21
- package/templates/latest/preview/index.html +125 -285
- package/templates/latest/public/favicon.svg +1 -0
- package/templates/latest/vite.config.ts +2 -8
- package/templates/latest/app/components/app-sidebar.tsx +0 -182
- package/templates/latest/app/components/chart-area-interactive.tsx +0 -290
- package/templates/latest/app/components/data-table.tsx +0 -807
- package/templates/latest/app/components/nav-documents.tsx +0 -92
- package/templates/latest/app/components/nav-main.tsx +0 -40
- package/templates/latest/app/components/nav-secondary.tsx +0 -42
- package/templates/latest/app/components/nav-user.tsx +0 -111
- package/templates/latest/app/components/section-cards.tsx +0 -102
- package/templates/latest/app/components/site-header.tsx +0 -28
- package/templates/latest/app/components/ui/avatar.tsx +0 -53
- package/templates/latest/app/components/ui/badge.tsx +0 -46
- package/templates/latest/app/components/ui/breadcrumb.tsx +0 -109
- package/templates/latest/app/components/ui/chart.tsx +0 -352
- package/templates/latest/app/components/ui/checkbox.tsx +0 -30
- package/templates/latest/app/components/ui/drawer.tsx +0 -139
- package/templates/latest/app/components/ui/select.tsx +0 -183
- package/templates/latest/app/components/ui/table.tsx +0 -117
- package/templates/latest/app/components/ui/toggle-group.tsx +0 -73
- package/templates/latest/app/components/ui/toggle.tsx +0 -47
- package/templates/latest/app/data/data.json +0 -614
- package/templates/latest/app/data/mock-payments.json +0 -122
- package/templates/latest/app/data/mock-transactions.json +0 -128
- package/templates/latest/app/hooks/use-bluecopa-user.ts +0 -37
- package/templates/latest/app/lib/utils.ts +0 -6
- package/templates/latest/app/routes/apitest.tsx +0 -2118
- package/templates/latest/app/routes/comments.tsx +0 -588
- package/templates/latest/app/routes/dashboard.tsx +0 -36
- package/templates/latest/app/routes/payments.tsx +0 -342
- package/templates/latest/app/routes/statements.tsx +0 -493
- package/templates/latest/app/routes/websocket.tsx +0 -450
- package/templates/latest/app/routes.tsx +0 -22
- package/templates/latest/dist/assets/__federation_expose_App-D-lv9y21.js +0 -161
- package/templates/latest/dist/assets/__federation_fn_import-CzfA7kmP.js +0 -438
- package/templates/latest/dist/assets/__federation_shared_react-Bp6HVBS4.js +0 -16
- package/templates/latest/dist/assets/__federation_shared_react-dom-BCcRGiYp.js +0 -17
- package/templates/latest/dist/assets/client-Dms8K6Dw.js +0 -78879
- package/templates/latest/dist/assets/index-BrhXrqF7.js +0 -60
- package/templates/latest/dist/assets/index-BzNimew1.js +0 -69
- package/templates/latest/dist/assets/index-DMFtQdNS.js +0 -412
- package/templates/latest/dist/assets/remoteEntry.css +0 -3996
- package/templates/latest/dist/assets/remoteEntry.js +0 -88
- package/templates/latest/dist/favicon.ico +0 -0
- package/templates/latest/dist/index.html +0 -19
|
@@ -1,936 +1,759 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CLAUDE.md — React MFE Boilerplate
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This file provides guidance to Claude Code when working with the Bluecopa React MFE (Micro-Frontend) boilerplate.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Project Overview
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
##
|
|
18
|
+
## Architecture
|
|
31
19
|
|
|
32
|
-
###
|
|
20
|
+
### Entry Points
|
|
33
21
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
41
|
+
### Key Files
|
|
53
42
|
|
|
54
|
-
|
|
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
|
-
|
|
55
|
+
### Directory Layout
|
|
57
56
|
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
84
|
+
## Tailwind v4 + `copa:` Prefix — CRITICAL
|
|
84
85
|
|
|
85
|
-
|
|
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
|
-
###
|
|
92
|
-
```tsx
|
|
93
|
-
import { cn } from "~/lib/utils"
|
|
88
|
+
### Prefix Order Rule
|
|
94
89
|
|
|
95
|
-
|
|
96
|
-
className?: string
|
|
97
|
-
children: React.ReactNode
|
|
98
|
-
}
|
|
90
|
+
**Prefix ALWAYS comes first**, then variants, then utility:
|
|
99
91
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
<div className={cn("base-styles", className)}>
|
|
103
|
-
{children}
|
|
104
|
-
</div>
|
|
105
|
-
)
|
|
106
|
-
}
|
|
92
|
+
```
|
|
93
|
+
{prefix}:{variants}:{utility}
|
|
107
94
|
```
|
|
108
95
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
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 [](https://www.npmjs.com/package/@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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
###
|
|
106
|
+
### CSS Architecture (app.css)
|
|
239
107
|
|
|
240
|
-
|
|
108
|
+
Three layers in order:
|
|
241
109
|
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
123
|
+
### Style Drivers
|
|
247
124
|
|
|
248
|
-
|
|
125
|
+
For reusable style tokens, define unprefixed in `component-style.ts`, consume prefixed from `style-drivers.ts`:
|
|
249
126
|
|
|
250
|
-
|
|
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
|
-
|
|
253
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
141
|
+
## @bluecopa/react — API Integration
|
|
275
142
|
|
|
276
|
-
|
|
143
|
+
React Query hooks wrapping `@bluecopa/core`. All data fetching uses these hooks.
|
|
277
144
|
|
|
278
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
151
|
+
### Configuration
|
|
324
152
|
|
|
325
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
164
|
+
### Environment Variables
|
|
339
165
|
|
|
340
|
-
```
|
|
341
|
-
|
|
166
|
+
```bash
|
|
167
|
+
VITE_BLUECOPA_API_URL=https://develop.bluecopa.com/api/v1
|
|
168
|
+
VITE_BLUECOPA_WORKSPACE_ID=prod
|
|
169
|
+
```
|
|
342
170
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
262
|
+
### Hook Usage Pattern
|
|
353
263
|
|
|
354
|
-
|
|
264
|
+
```tsx
|
|
265
|
+
import { useDataset } from "@bluecopa/react";
|
|
355
266
|
|
|
356
|
-
|
|
267
|
+
function MyPage() {
|
|
268
|
+
const { data, isLoading, error, refetch } = useDataset("dataset-id", { limit: 100 });
|
|
357
269
|
|
|
358
|
-
|
|
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
|
-
|
|
377
|
-
|
|
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
|
|
391
|
-
|
|
392
|
-
### `useUser(options?)`
|
|
279
|
+
## @bluecopa/core — API SDK
|
|
393
280
|
|
|
394
|
-
|
|
281
|
+
Low-level TypeScript SDK. Prefer `@bluecopa/react` hooks in components, use `copaApi` directly only when needed outside React.
|
|
395
282
|
|
|
396
|
-
|
|
397
|
-
- `options` (optional): Query options extending TanStack React Query's `UseQueryOptions`
|
|
283
|
+
### Authentication
|
|
398
284
|
|
|
399
|
-
|
|
400
|
-
- `
|
|
401
|
-
- `
|
|
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
|
-
###
|
|
289
|
+
### Direct API Usage
|
|
406
290
|
|
|
407
|
-
|
|
291
|
+
```typescript
|
|
292
|
+
import { copaApi } from "@bluecopa/react";
|
|
408
293
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
|
|
301
|
+
```typescript
|
|
302
|
+
import { copaUtils } from "@bluecopa/react";
|
|
422
303
|
|
|
423
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
347
|
+
## Dream Light Design System
|
|
437
348
|
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
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
|
-
|
|
354
|
+
From either registry via shadcn CLI:
|
|
449
355
|
|
|
450
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
368
|
+
### Available Dream Light Components (70+)
|
|
465
369
|
|
|
466
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
376
|
+
**Containers**: card, section-card, dialog, sheet, side-panel, popover, accordion, scroll-area, separator, stack, kanban, tree-menu, topbar
|
|
479
377
|
|
|
480
|
-
|
|
378
|
+
**Layout**: scaffold, page-container, section, page-header
|
|
481
379
|
|
|
482
|
-
**
|
|
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
|
-
**
|
|
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
|
-
###
|
|
384
|
+
### Design Tokens
|
|
493
385
|
|
|
494
|
-
|
|
386
|
+
#### Colors (OKLCH)
|
|
495
387
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
-
|
|
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
|
-
|
|
501
|
-
-
|
|
502
|
-
-
|
|
503
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
511
|
-
- `workbookType`: Type of workbooks to fetch
|
|
512
|
-
- `options` (optional): Query options
|
|
435
|
+
#### Custom Typography Utilities (defined in app.css)
|
|
513
436
|
|
|
514
|
-
|
|
515
|
-
-
|
|
516
|
-
-
|
|
517
|
-
-
|
|
518
|
-
-
|
|
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
|
-
|
|
445
|
+
#### Spacing
|
|
521
446
|
|
|
522
|
-
|
|
447
|
+
```css
|
|
448
|
+
--radius: 1rem;
|
|
449
|
+
--card-radius: 1.25rem;
|
|
450
|
+
--rounded-input: 12px;
|
|
523
451
|
|
|
524
|
-
|
|
525
|
-
-
|
|
526
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
466
|
+
### Chart Colors
|
|
537
467
|
|
|
538
|
-
|
|
539
|
-
-
|
|
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
|
-
|
|
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
|
-
###
|
|
478
|
+
### Vite + Module Federation
|
|
548
479
|
|
|
549
|
-
|
|
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
|
-
**
|
|
552
|
-
-
|
|
553
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
494
|
+
- Path alias: `~/` → `./app/` (via `tsconfig.json` paths + `vite-tsconfig-paths`)
|
|
495
|
+
- Target: ES2020, JSX: react-jsx, strict mode
|
|
562
496
|
|
|
563
|
-
|
|
497
|
+
### Docker
|
|
564
498
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
499
|
+
```bash
|
|
500
|
+
docker build -t bluecopa-mfe .
|
|
501
|
+
# Serves dist/ via lightweight HTTP server
|
|
502
|
+
```
|
|
568
503
|
|
|
569
|
-
|
|
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
|
-
###
|
|
506
|
+
### How the Host App Loads This MFE
|
|
576
507
|
|
|
577
|
-
|
|
508
|
+
```javascript
|
|
509
|
+
// 1. Load Module Federation entry
|
|
510
|
+
System.import("__copa_ext_app__react_route/App");
|
|
578
511
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
533
|
+
### Manual Mount API (for non-single-spa hosts)
|
|
592
534
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
- `options` (optional): Query options
|
|
535
|
+
```typescript
|
|
536
|
+
import { manualMount, manualUnmount } from "./single-spa";
|
|
596
537
|
|
|
597
|
-
|
|
598
|
-
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
548
|
+
## Charts (ECharts)
|
|
604
549
|
|
|
605
|
-
|
|
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
|
-
|
|
608
|
-
- `workflowId`: ID of the workflow
|
|
609
|
-
- `payload`: Request payload
|
|
610
|
-
- `options` (optional): Query options
|
|
552
|
+
### Architecture
|
|
611
553
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
- `
|
|
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
|
-
###
|
|
564
|
+
### Available Chart Components
|
|
619
565
|
|
|
620
|
-
|
|
566
|
+
#### BarChart
|
|
621
567
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
611
|
+
#### BaseChart (escape hatch)
|
|
635
612
|
|
|
636
|
-
|
|
637
|
-
- `workbookId`: ID of the workbook
|
|
638
|
-
- `options` (optional): Query options
|
|
613
|
+
For custom ECharts configurations not covered by BarChart/DonutChart:
|
|
639
614
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
-
###
|
|
633
|
+
### Chart Colors
|
|
647
634
|
|
|
648
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
- `
|
|
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
|
-
|
|
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
|
-
###
|
|
647
|
+
### Adding New Chart Types
|
|
661
648
|
|
|
662
|
-
|
|
649
|
+
To add a new chart type (e.g., LineChart, PieChart), follow the pattern in `bar-chart.tsx`:
|
|
663
650
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
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
|
-
|
|
660
|
+
Uses [Sonner](https://sonner.emilkowal.dev/) for toast notifications. The `<Toaster />` component is rendered in `app.tsx`.
|
|
676
661
|
|
|
677
|
-
###
|
|
662
|
+
### Usage
|
|
678
663
|
|
|
679
664
|
```tsx
|
|
680
|
-
|
|
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
|
-
|
|
667
|
+
// Success
|
|
668
|
+
toast.success("Record saved", { description: "Changes applied." });
|
|
692
669
|
|
|
693
|
-
|
|
670
|
+
// Error
|
|
671
|
+
toast.error("Failed to save", { description: "Check your connection." });
|
|
694
672
|
|
|
695
|
-
|
|
696
|
-
|
|
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
|
-
|
|
676
|
+
// Info
|
|
677
|
+
toast.info("New data available");
|
|
707
678
|
|
|
708
|
-
|
|
679
|
+
// With action
|
|
680
|
+
toast("Item deleted", {
|
|
681
|
+
action: { label: "Undo", onClick: () => undoDelete() },
|
|
682
|
+
});
|
|
709
683
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
723
|
-
|
|
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
|
-
|
|
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
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
###
|
|
756
|
-
|
|
757
|
-
-
|
|
758
|
-
-
|
|
759
|
-
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
-
|
|
765
|
-
-
|
|
766
|
-
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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.
|