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,218 +1,101 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { createRoot, type Root } from "react-dom/client";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
MemoryRouter,
|
|
6
|
-
BrowserRouter,
|
|
7
|
-
useNavigate,
|
|
8
|
-
useLocation,
|
|
9
|
-
} from "react-router-dom";
|
|
3
|
+
import { BrowserRouter } from "react-router-dom";
|
|
10
4
|
import App from "./app";
|
|
11
|
-
import type {
|
|
5
|
+
import type { MfeProps } from "~/types";
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
interface
|
|
7
|
+
/** Props passed by the host app when mounting this MFE. */
|
|
8
|
+
interface MountProps extends MfeProps {
|
|
9
|
+
/** The DOM element to render into (required). */
|
|
15
10
|
domElement: HTMLElement;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
/** Aliases the host may use instead of domElement. */
|
|
12
|
+
container?: HTMLElement;
|
|
13
|
+
element?: HTMLElement;
|
|
14
|
+
root?: HTMLElement;
|
|
15
|
+
/** Alias for basename used by some hosts. */
|
|
16
|
+
baseUrl?: string;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
let
|
|
19
|
+
let reactRoot: Root | null = null;
|
|
25
20
|
|
|
26
|
-
// Helper function to extract path from browser URL
|
|
27
|
-
function getInitialPath(basename?: string): string {
|
|
28
|
-
const currentPath = window.location.pathname;
|
|
29
|
-
const search = window.location.search;
|
|
30
|
-
const hash = window.location.hash;
|
|
31
|
-
|
|
32
|
-
// If basename is provided, extract the path after basename
|
|
33
|
-
let pathAfterBasename = currentPath;
|
|
34
|
-
if (basename && currentPath.startsWith(basename)) {
|
|
35
|
-
pathAfterBasename = currentPath.slice(basename.length) || "/";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Combine path, search params, and hash
|
|
39
|
-
return pathAfterBasename + search + hash;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Component to sync MemoryRouter navigation with browser URL
|
|
43
|
-
const RouterSync: React.FC<{ basename?: string }> = ({ basename }) => {
|
|
44
|
-
const location = useLocation();
|
|
45
|
-
const navigate = useNavigate();
|
|
46
|
-
|
|
47
|
-
useEffect(() => {
|
|
48
|
-
// Get the current path from MemoryRouter
|
|
49
|
-
const mainAppPath = window.location.pathname;
|
|
50
|
-
if (mainAppPath.includes("/apps/ext/")) {
|
|
51
|
-
const currentPath = location.pathname;
|
|
52
|
-
const search = location.search || "";
|
|
53
|
-
const hash = location.hash || "";
|
|
54
|
-
|
|
55
|
-
// Calculate the full URL path
|
|
56
|
-
const basenamePath = basename || "";
|
|
57
|
-
const fullPath = basenamePath + currentPath + search + hash;
|
|
58
|
-
|
|
59
|
-
// Update browser URL without reloading the page
|
|
60
|
-
const currentBrowserPath =
|
|
61
|
-
window.location.pathname +
|
|
62
|
-
window.location.search +
|
|
63
|
-
window.location.hash;
|
|
64
|
-
if (currentBrowserPath !== fullPath) {
|
|
65
|
-
// Use pushState to maintain browser history for back/forward buttons
|
|
66
|
-
window.history.pushState(
|
|
67
|
-
{ ...window.history.state, path: fullPath },
|
|
68
|
-
"",
|
|
69
|
-
fullPath
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}, [location.pathname, location.search, location.hash, basename]);
|
|
74
|
-
|
|
75
|
-
// Listen for browser navigation (back/forward buttons)
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
const handlePopState = () => {
|
|
78
|
-
const mainAppPath = window.location.pathname;
|
|
79
|
-
// If current path does not contain /apps/ext/, clean up all /apps/ext/ references
|
|
80
|
-
if (mainAppPath.includes("/apps/ext/")) {
|
|
81
|
-
const currentPath = window.location.pathname;
|
|
82
|
-
const search = window.location.search || "";
|
|
83
|
-
const hash = window.location.hash || "";
|
|
84
|
-
const basenamePath = basename || "";
|
|
85
|
-
|
|
86
|
-
// Extract the path after basename
|
|
87
|
-
let pathToNavigate = currentPath;
|
|
88
|
-
if (basenamePath && currentPath.startsWith(basenamePath)) {
|
|
89
|
-
pathToNavigate = currentPath.slice(basenamePath.length) || "/";
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Combine with search and hash
|
|
93
|
-
const fullPathToNavigate = pathToNavigate + search + hash;
|
|
94
|
-
const currentRouterPath =
|
|
95
|
-
location.pathname + (location.search || "") + (location.hash || "");
|
|
96
|
-
|
|
97
|
-
// Navigate MemoryRouter to match browser URL
|
|
98
|
-
if (currentRouterPath !== fullPathToNavigate) {
|
|
99
|
-
navigate(fullPathToNavigate, { replace: true });
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
window.removeEventListener("popstate", handlePopState);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
window.addEventListener("popstate", handlePopState);
|
|
107
|
-
return () => window.removeEventListener("popstate", handlePopState);
|
|
108
|
-
}, [navigate, location.pathname, location.search, location.hash, basename]);
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Root component wrapper that handles routing
|
|
114
21
|
const MicrofrontendRoot: React.FC<{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}> = ({ isMicroFrontend, props }) => {
|
|
118
|
-
// Use MemoryRouter for micro-frontend to avoid conflicts with host routing
|
|
119
|
-
// Use BrowserRouter for standalone mode
|
|
120
|
-
const Router = isMicroFrontend ? MemoryRouter : BrowserRouter;
|
|
121
|
-
|
|
122
|
-
// Get initial path from browser URL for MemoryRouter
|
|
123
|
-
const initialPath = isMicroFrontend ? getInitialPath(props?.basename) : "/";
|
|
124
|
-
|
|
125
|
-
const routerProps = isMicroFrontend
|
|
126
|
-
? { initialEntries: [initialPath], initialIndex: 0 }
|
|
127
|
-
: { basename: props?.basename };
|
|
128
|
-
|
|
22
|
+
props?: MfeProps;
|
|
23
|
+
}> = ({ props }) => {
|
|
129
24
|
return (
|
|
130
|
-
<
|
|
131
|
-
{isMicroFrontend && <RouterSync basename={props?.basename} />}
|
|
25
|
+
<BrowserRouter basename={props?.basename}>
|
|
132
26
|
<App {...props} />
|
|
133
|
-
</
|
|
27
|
+
</BrowserRouter>
|
|
134
28
|
);
|
|
135
29
|
};
|
|
136
30
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return <div>Something went wrong loading Microfrontend</div>;
|
|
147
|
-
},
|
|
148
|
-
renderType: "createRoot",
|
|
149
|
-
domElementGetter: () => {
|
|
150
|
-
const el = document.getElementById(
|
|
151
|
-
"single-spa-application:bluecopa-preview"
|
|
31
|
+
/**
|
|
32
|
+
* Resolves the DOM element from the various prop aliases the host may pass.
|
|
33
|
+
* The host sends domElement, container, element, root — accept any of them.
|
|
34
|
+
*/
|
|
35
|
+
function resolveDomElement(props: MountProps): HTMLElement {
|
|
36
|
+
const el = props.domElement || props.container || props.element || props.root;
|
|
37
|
+
if (!el) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
"domElement (or container/element/root) is required for mounting the MFE",
|
|
152
40
|
);
|
|
153
|
-
if (!el)
|
|
154
|
-
throw new Error(
|
|
155
|
-
"Mount target #single-spa-application:bluecopa-preview not found"
|
|
156
|
-
);
|
|
157
|
-
return el;
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Export the single-spa lifecycle functions
|
|
162
|
-
export const { mount, unmount, bootstrap } = lifecycles;
|
|
163
|
-
|
|
164
|
-
// Export a manual mount function for direct usage
|
|
165
|
-
export const manualMount = async (props: LifecycleProps) => {
|
|
166
|
-
if (!props.domElement) {
|
|
167
|
-
throw new Error("domElement is required for mounting the application");
|
|
168
41
|
}
|
|
42
|
+
return el;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Mount the MFE into a given DOM element.
|
|
47
|
+
* This is the primary entry point called by the host's single-spa-config.
|
|
48
|
+
*/
|
|
49
|
+
export async function mount(props: MountProps): Promise<void> {
|
|
50
|
+
const domElement = resolveDomElement(props);
|
|
169
51
|
|
|
170
52
|
try {
|
|
171
|
-
// Clean up
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
53
|
+
// Clean up previous root if remounting
|
|
54
|
+
if (reactRoot) {
|
|
55
|
+
reactRoot.unmount();
|
|
56
|
+
reactRoot = null;
|
|
175
57
|
}
|
|
176
58
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
// Mount the application with the provided basename as micro-frontend
|
|
181
|
-
root.render(
|
|
59
|
+
reactRoot = createRoot(domElement);
|
|
60
|
+
reactRoot.render(
|
|
182
61
|
<MicrofrontendRoot
|
|
183
|
-
isMicroFrontend={true}
|
|
184
62
|
props={{
|
|
185
|
-
basename: props.basename ||
|
|
63
|
+
basename: props.basename || props.baseUrl,
|
|
64
|
+
accessToken: props.accessToken,
|
|
65
|
+
workspaceId: props.workspaceId,
|
|
66
|
+
apiBaseUrl: props.apiBaseUrl,
|
|
67
|
+
userId: props.userId,
|
|
186
68
|
}}
|
|
187
|
-
|
|
69
|
+
/>,
|
|
188
70
|
);
|
|
189
|
-
|
|
190
|
-
console.log("Microfrontend mounted successfully");
|
|
191
|
-
return;
|
|
192
71
|
} catch (error) {
|
|
193
|
-
console.error("Failed to mount
|
|
72
|
+
console.error("Failed to mount MFE:", error);
|
|
194
73
|
throw error;
|
|
195
74
|
}
|
|
196
|
-
}
|
|
75
|
+
}
|
|
197
76
|
|
|
198
|
-
|
|
199
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Unmount the MFE and clean up the React tree.
|
|
79
|
+
*/
|
|
80
|
+
export async function unmount(): Promise<void> {
|
|
200
81
|
try {
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
|
|
82
|
+
if (reactRoot) {
|
|
83
|
+
reactRoot.unmount();
|
|
84
|
+
reactRoot = null;
|
|
204
85
|
}
|
|
205
|
-
console.log("Microfrontend unmounted successfully");
|
|
206
|
-
return Promise.resolve();
|
|
207
86
|
} catch (error) {
|
|
208
|
-
console.error("Failed to unmount
|
|
209
|
-
|
|
87
|
+
console.error("Failed to unmount MFE:", error);
|
|
88
|
+
throw error;
|
|
210
89
|
}
|
|
211
|
-
}
|
|
90
|
+
}
|
|
212
91
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Bootstrap — no-op, required by single-spa contract.
|
|
94
|
+
*/
|
|
95
|
+
export async function bootstrap(): Promise<void> {}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Default export provides the same lifecycle for Module Federation consumers
|
|
99
|
+
* that import the default rather than named exports.
|
|
100
|
+
*/
|
|
101
|
+
export default { mount, unmount, bootstrap };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Props passed to the MFE root component from the host app (single-spa customProps or manualMount). */
|
|
2
|
+
export interface MfeProps {
|
|
3
|
+
apiBaseUrl?: string;
|
|
4
|
+
workspaceId?: string;
|
|
5
|
+
accessToken?: string;
|
|
6
|
+
userId?: string;
|
|
7
|
+
basename?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Normalized user object provided by AppContext. */
|
|
11
|
+
export type AppUser = {
|
|
12
|
+
userId?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
userExists?: boolean;
|
|
15
|
+
settings?: Record<string, unknown>;
|
|
16
|
+
workspaceIds?: string[];
|
|
17
|
+
currentWorkspace?: unknown;
|
|
18
|
+
teams?: unknown[];
|
|
19
|
+
userTeamProperties?: unknown[];
|
|
20
|
+
email?: string | null;
|
|
21
|
+
role?: string;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Workspace data settings derived from user/workspace API (currency, timezone, date format, etc.) */
|
|
26
|
+
export type WorkspaceDataSettings = {
|
|
27
|
+
currency: string;
|
|
28
|
+
timezone: string;
|
|
29
|
+
dateFormat: string;
|
|
30
|
+
fiscalYear: { yearType: string; startMonth?: number; startDate?: number };
|
|
31
|
+
numberSettings: {
|
|
32
|
+
numberSystem: string;
|
|
33
|
+
defaultPrecision: number;
|
|
34
|
+
nullPlaceholder?: string;
|
|
35
|
+
};
|
|
36
|
+
weekStartDay: string;
|
|
37
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IServerSideDatasource,
|
|
3
|
+
IServerSideGetRowsParams,
|
|
4
|
+
} from "ag-grid-community";
|
|
5
|
+
import { copaApi } from "@bluecopa/react";
|
|
6
|
+
|
|
7
|
+
interface CreateDatasourceOptions {
|
|
8
|
+
tableId: string;
|
|
9
|
+
totalCount?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createServerSideDatasource({
|
|
13
|
+
tableId,
|
|
14
|
+
totalCount,
|
|
15
|
+
}: CreateDatasourceOptions): IServerSideDatasource {
|
|
16
|
+
return {
|
|
17
|
+
getRows: async (params: IServerSideGetRowsParams) => {
|
|
18
|
+
const {
|
|
19
|
+
startRow = 0,
|
|
20
|
+
endRow = 50,
|
|
21
|
+
sortModel,
|
|
22
|
+
filterModel,
|
|
23
|
+
} = params.request;
|
|
24
|
+
const limit = endRow - startRow;
|
|
25
|
+
const offset = startRow;
|
|
26
|
+
|
|
27
|
+
const order_by = sortModel?.[0]?.colId;
|
|
28
|
+
const order = sortModel?.[0]?.sort as "asc" | "desc" | undefined;
|
|
29
|
+
|
|
30
|
+
const filters = filterModel
|
|
31
|
+
? Object.entries(filterModel).reduce(
|
|
32
|
+
(acc, [col, filter]) => {
|
|
33
|
+
acc[col] = (filter as any).filter;
|
|
34
|
+
return acc;
|
|
35
|
+
},
|
|
36
|
+
{} as Record<string, unknown>,
|
|
37
|
+
)
|
|
38
|
+
: undefined;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const response = await copaApi.inputTable.getRows(tableId, {
|
|
42
|
+
limit,
|
|
43
|
+
offset,
|
|
44
|
+
order,
|
|
45
|
+
order_by,
|
|
46
|
+
filters,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const rows =
|
|
50
|
+
(response as any)?.data ||
|
|
51
|
+
(response as any)?.rows ||
|
|
52
|
+
[];
|
|
53
|
+
const rowCount =
|
|
54
|
+
totalCount ?? (response as any)?.totalCount ?? undefined;
|
|
55
|
+
|
|
56
|
+
params.success({ rowData: rows, rowCount });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("[AG Grid Datasource] Failed:", error);
|
|
59
|
+
params.fail();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LicenseManager } from "ag-grid-enterprise";
|
|
2
|
+
|
|
3
|
+
export function initAgGridLicense() {
|
|
4
|
+
const key = import.meta.env.VITE_AG_GRID_LICENSE_KEY;
|
|
5
|
+
if (!key) {
|
|
6
|
+
console.warn(
|
|
7
|
+
"[AG Grid] No license key found (VITE_AG_GRID_LICENSE_KEY). Enterprise features will show watermarks.",
|
|
8
|
+
);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
LicenseManager.setLicenseKey(key);
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Unprefixed style tokens for component composition.
|
|
2
|
+
// These get auto-prefixed by style-drivers.ts before being applied.
|
|
3
|
+
export const styles = {
|
|
4
|
+
card: "rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
5
|
+
cardHeader: "flex flex-col space-y-1.5 p-6",
|
|
6
|
+
cardContent: "p-6 pt-0",
|
|
7
|
+
} as const;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { styles } from "./component-style";
|
|
2
|
+
|
|
3
|
+
const PREFIX = "copa";
|
|
4
|
+
|
|
5
|
+
function prefixClasses(classString: string): string {
|
|
6
|
+
return classString
|
|
7
|
+
.split(" ")
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
.map((cls) => {
|
|
10
|
+
// Handle variant prefixes (hover:, focus:, data-[...]:, etc.)
|
|
11
|
+
const parts = cls.split(":");
|
|
12
|
+
const utility = parts.pop()!;
|
|
13
|
+
const variants = parts;
|
|
14
|
+
// Reconstruct: variants + copa: + utility
|
|
15
|
+
return [...variants, `${PREFIX}:${utility}`].join(":");
|
|
16
|
+
})
|
|
17
|
+
.join(" ");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const prefixedStyles = Object.fromEntries(
|
|
21
|
+
Object.entries(styles).map(([key, value]) => [key, prefixClasses(value)])
|
|
22
|
+
) as typeof styles;
|
|
23
|
+
|
|
24
|
+
export { prefixClasses };
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"css": "app/app.css",
|
|
9
9
|
"baseColor": "gray",
|
|
10
10
|
"cssVariables": true,
|
|
11
|
-
"prefix": ""
|
|
11
|
+
"prefix": "copa"
|
|
12
12
|
},
|
|
13
13
|
"iconLibrary": "lucide",
|
|
14
14
|
"aliases": {
|
|
15
15
|
"components": "~/components",
|
|
16
|
-
"utils": "~/
|
|
16
|
+
"utils": "~/utils/utils",
|
|
17
17
|
"ui": "~/components/ui",
|
|
18
|
-
"lib": "~/
|
|
18
|
+
"lib": "~/utils",
|
|
19
19
|
"hooks": "~/hooks"
|
|
20
20
|
},
|
|
21
21
|
"registries": {
|
|
@@ -2,13 +2,41 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Bluecopa - AI-powered Finance Automation</title>
|
|
8
8
|
<meta name="description" content="AI-powered close automation for modern finance teams. Streamline your order-to-cash, procure-to-pay, and record-to-report processes." />
|
|
9
|
+
<style>
|
|
10
|
+
/*
|
|
11
|
+
* Standalone preflight — replaces Tailwind's preflight for dev mode.
|
|
12
|
+
* This file is NOT used in MFE mode (host provides its own resets).
|
|
13
|
+
*/
|
|
14
|
+
*, *::before, *::after { box-sizing: border-box; border-width: 0; border-style: solid; margin: 0; padding: 0; }
|
|
15
|
+
html { height: 100%; line-height: 1.5; -webkit-text-size-adjust: 100%; tab-size: 4; }
|
|
16
|
+
body { height: 100%; line-height: inherit; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
|
17
|
+
#root { height: 100%; }
|
|
18
|
+
h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; }
|
|
19
|
+
a { color: inherit; text-decoration: inherit; }
|
|
20
|
+
ol, ul, menu { list-style: none; }
|
|
21
|
+
img, svg, video, canvas, audio, iframe, embed, object { display: block; max-width: 100%; }
|
|
22
|
+
button, input, optgroup, select, textarea { font: inherit; color: inherit; }
|
|
23
|
+
button { cursor: pointer; background: transparent; }
|
|
24
|
+
table { border-collapse: collapse; border-spacing: 0; text-indent: 0; border-color: inherit; }
|
|
25
|
+
hr { height: 0; color: inherit; border-top-width: 1px; }
|
|
26
|
+
p, blockquote, dl, dd, figure, fieldset, legend, pre { margin: 0; }
|
|
27
|
+
</style>
|
|
9
28
|
</head>
|
|
10
29
|
<body>
|
|
11
|
-
<div id="root"
|
|
30
|
+
<div id="root">
|
|
31
|
+
<!-- Inline loader: visible instantly before JS/CSS loads, replaced when React mounts -->
|
|
32
|
+
<div id="initial-loader" style="display:flex;align-items:center;justify-content:center;height:100vh;width:100%;background:#f8f8fa;font-family:'Satoshi',system-ui,sans-serif;">
|
|
33
|
+
<div style="display:flex;flex-direction:column;align-items:center;gap:16px;">
|
|
34
|
+
<div style="width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#3548ff;border-radius:50%;animation:init-spin 0.7s linear infinite;"></div>
|
|
35
|
+
<span style="color:#6b7280;font-size:0.835rem;">Loading...</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<style>@keyframes init-spin{to{transform:rotate(360deg)}}</style>
|
|
39
|
+
</div>
|
|
12
40
|
<script type="module" src="/app/main.tsx"></script>
|
|
13
41
|
</body>
|
|
14
42
|
</html>
|