create-bluecopa-react-app 1.0.4 → 1.0.5
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/package.json +1 -1
- package/templates/latest/AGENT.md +13 -15
- package/templates/latest/README.md +2 -2
- package/templates/latest/clean.sh +1 -0
- package/templates/latest/setup.sh +5 -4
- package/templates/latest/src/App.tsx +11 -7
- package/templates/latest/src/components/charts/AreaChart.tsx +80 -0
- package/templates/latest/src/components/charts/DonutChart.tsx +73 -0
- package/templates/latest/src/components/charts/SparkAreaChart.tsx +52 -0
- package/templates/latest/src/components/layout/dashboard-header.tsx +1 -1
- package/templates/latest/src/components/layout/dashboard-layout.tsx +19 -11
- package/templates/latest/src/components/{page → layout}/navbar.tsx +2 -0
- package/templates/latest/src/components/layout/sidebar.tsx +2 -1
- package/templates/latest/src/components/page/dashboard/DashboardMetrics.tsx +97 -0
- package/templates/latest/src/components/page/dashboard/PaymentMethodsAnalysis.tsx +182 -0
- package/templates/latest/src/components/page/dashboard/RevenueAnalytics.tsx +505 -0
- package/templates/latest/src/components/page/dashboard/SalesAnalytics.tsx +313 -0
- package/templates/latest/src/components/page/dashboard/TransactionsTable.tsx +256 -0
- package/templates/latest/src/components/page/dashboard/dashboard-utils.ts +147 -0
- package/templates/latest/src/components/page/dashboard/dashboard.tsx +185 -0
- package/templates/latest/src/components/ui/bluecopa-logo.tsx +3 -0
- package/templates/latest/src/components/ui/label.tsx +0 -2
- package/templates/latest/src/components/ui/select.tsx +0 -2
- package/templates/latest/src/pages/Dashboard.tsx +1 -1
- package/templates/latest/src/single-spa.tsx +38 -28
- package/templates/latest/tailwind.config.js +1 -2
- package/templates/latest/tsconfig.app.json +6 -0
- package/templates/latest/tsconfig.json +6 -6
- package/templates/latest/tsconfig.node.json +5 -1
- package/templates/latest/vite.config.ts +1 -1
- package/templates/latest/src/components/page/dashboard.tsx +0 -1506
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Helper function to render query state
|
|
2
|
+
export const renderQueryState = (query: any, title: string) => {
|
|
3
|
+
if (query.isLoading) {
|
|
4
|
+
return {
|
|
5
|
+
status: 'loading',
|
|
6
|
+
message: `${title}: Loading...`
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (query.isError) {
|
|
11
|
+
return {
|
|
12
|
+
status: 'error',
|
|
13
|
+
message: `${title}: Error`
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (query.isSuccess) {
|
|
18
|
+
return {
|
|
19
|
+
status: 'success',
|
|
20
|
+
message: `${title}: Success`
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
status: 'idle',
|
|
26
|
+
message: `${title}: Idle`
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Process data for ecommerce metrics and charts
|
|
31
|
+
export const processOrderData = (rawData: any[]) => {
|
|
32
|
+
return rawData.reduce((acc: any, transaction: any) => {
|
|
33
|
+
const existing = acc.find((item: any) => item.category === transaction.category);
|
|
34
|
+
if (existing) {
|
|
35
|
+
existing.total += transaction.total_amount;
|
|
36
|
+
existing.count += 1;
|
|
37
|
+
} else {
|
|
38
|
+
acc.push({
|
|
39
|
+
category: transaction.category,
|
|
40
|
+
total: transaction.total_amount,
|
|
41
|
+
count: 1
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return acc;
|
|
45
|
+
}, []);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const processPaymentMethodData = (rawData: any[]) => {
|
|
49
|
+
return rawData.reduce((acc: any, transaction: any) => {
|
|
50
|
+
const existing = acc.find((item: any) => item.method === transaction.payment_method);
|
|
51
|
+
if (existing) {
|
|
52
|
+
existing.total += transaction.total_amount;
|
|
53
|
+
existing.count += 1;
|
|
54
|
+
} else {
|
|
55
|
+
acc.push({
|
|
56
|
+
method: transaction.payment_method,
|
|
57
|
+
total: transaction.total_amount,
|
|
58
|
+
count: 1
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return acc;
|
|
62
|
+
}, []);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const processMonthlyRevenueData = (rawData: any[]) => {
|
|
66
|
+
return rawData.reduce((acc: any, transaction: any) => {
|
|
67
|
+
if (!transaction.transaction_date) return acc;
|
|
68
|
+
|
|
69
|
+
const date = new Date(transaction.transaction_date);
|
|
70
|
+
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
|
71
|
+
|
|
72
|
+
const existing = acc.find((item: any) => item.month === monthKey);
|
|
73
|
+
if (existing) {
|
|
74
|
+
existing.revenue += transaction.total_amount;
|
|
75
|
+
existing.orders += 1;
|
|
76
|
+
} else {
|
|
77
|
+
acc.push({
|
|
78
|
+
month: monthKey,
|
|
79
|
+
revenue: transaction.total_amount,
|
|
80
|
+
orders: 1
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return acc;
|
|
84
|
+
}, []).sort((a: any, b: any) => a.month.localeCompare(b.month));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const processRecentTransactions = (rawData: any[]) => {
|
|
88
|
+
return rawData
|
|
89
|
+
.slice(0, 10)
|
|
90
|
+
.map((transaction: any) => ({
|
|
91
|
+
id: transaction.transaction_id,
|
|
92
|
+
customer: transaction.customer_name || 'N/A',
|
|
93
|
+
amount: transaction.total_amount,
|
|
94
|
+
category: transaction.category,
|
|
95
|
+
payment_method: transaction.payment_method,
|
|
96
|
+
date: transaction.transaction_date ? new Date(transaction.transaction_date).toLocaleDateString() : 'N/A'
|
|
97
|
+
}));
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const calculateMetrics = (rawData: any[]) => {
|
|
101
|
+
const totalOrders = rawData.length || 0;
|
|
102
|
+
const totalRevenue = rawData.reduce((sum: number, t: any) => sum + t.total_amount, 0);
|
|
103
|
+
const averageOrderValue = totalOrders > 0 ? totalRevenue / totalOrders : 0;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
totalOrders,
|
|
107
|
+
totalRevenue,
|
|
108
|
+
averageOrderValue
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const createEcommerceMetrics = (totalRevenue: number, totalOrders: number, averageOrderValue: number) => {
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
title: 'Total Revenue',
|
|
116
|
+
value: `$${totalRevenue.toLocaleString()}`,
|
|
117
|
+
change: '+12.5%',
|
|
118
|
+
trend: 'up' as const,
|
|
119
|
+
icon: 'DollarSign',
|
|
120
|
+
color: 'text-green-600'
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
title: 'Orders',
|
|
124
|
+
value: totalOrders.toLocaleString(),
|
|
125
|
+
change: '+8.2%',
|
|
126
|
+
trend: 'up' as const,
|
|
127
|
+
icon: 'ShoppingCart',
|
|
128
|
+
color: 'text-blue-600'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
title: 'Average Order Value',
|
|
132
|
+
value: `$${averageOrderValue.toFixed(2)}`,
|
|
133
|
+
change: '+3.1%',
|
|
134
|
+
trend: 'up' as const,
|
|
135
|
+
icon: 'TrendingUp',
|
|
136
|
+
color: 'text-purple-600'
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
title: 'Total Customers',
|
|
140
|
+
value: '2,847',
|
|
141
|
+
change: '+15.3%',
|
|
142
|
+
trend: 'up' as const,
|
|
143
|
+
icon: 'Users',
|
|
144
|
+
color: 'text-orange-600'
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { Alert, AlertDescription } from '@/components/ui/alert'
|
|
5
|
+
import { useMetricDataDemo, useDatasetDataDemo, useUserData } from '@/hooks/use-api'
|
|
6
|
+
import {
|
|
7
|
+
RefreshCw,
|
|
8
|
+
Loader2,
|
|
9
|
+
AlertCircle,
|
|
10
|
+
CheckCircle,
|
|
11
|
+
Clock
|
|
12
|
+
} from 'lucide-react'
|
|
13
|
+
import { DashboardMetrics } from './DashboardMetrics'
|
|
14
|
+
import { SalesAnalytics } from './SalesAnalytics'
|
|
15
|
+
import { PaymentMethodsAnalysis } from './PaymentMethodsAnalysis'
|
|
16
|
+
import { TransactionsTable } from './TransactionsTable'
|
|
17
|
+
import { RevenueAnalytics } from './RevenueAnalytics'
|
|
18
|
+
import {
|
|
19
|
+
processOrderData,
|
|
20
|
+
processPaymentMethodData,
|
|
21
|
+
processMonthlyRevenueData,
|
|
22
|
+
processRecentTransactions,
|
|
23
|
+
calculateMetrics,
|
|
24
|
+
createEcommerceMetrics,
|
|
25
|
+
renderQueryState as renderQueryStateUtil
|
|
26
|
+
} from './dashboard-utils'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export default function Dashboard() {
|
|
30
|
+
const metricQuery = useMetricDataDemo()
|
|
31
|
+
const datasetQuery = useDatasetDataDemo()
|
|
32
|
+
const userQuery = useUserData()
|
|
33
|
+
|
|
34
|
+
// Process data for ecommerce metrics and charts
|
|
35
|
+
const rawApiData = datasetQuery.data?.data?.data || []
|
|
36
|
+
|
|
37
|
+
// Fallback sample data when API returns empty data
|
|
38
|
+
const sampleTransactionData = [
|
|
39
|
+
{ transaction_id: '001', customer_name: 'John Doe', total_amount: 1250, category: 'Electronics', payment_method: 'Credit Card', transaction_date: '2024-01-15', state: 'CA' },
|
|
40
|
+
{ transaction_id: '002', customer_name: 'Jane Smith', total_amount: 780, category: 'Clothing', payment_method: 'PayPal', transaction_date: '2024-01-18', state: 'NY' },
|
|
41
|
+
{ transaction_id: '003', customer_name: 'Mike Johnson', total_amount: 2100, category: 'Electronics', payment_method: 'Credit Card', transaction_date: '2024-02-02', state: 'TX' },
|
|
42
|
+
{ transaction_id: '004', customer_name: 'Sarah Wilson', total_amount: 450, category: 'Books', payment_method: 'Debit Card', transaction_date: '2024-02-05', state: 'FL' },
|
|
43
|
+
{ transaction_id: '005', customer_name: 'David Brown', total_amount: 890, category: 'Sports', payment_method: 'Credit Card', transaction_date: '2024-02-10', state: 'CA' },
|
|
44
|
+
{ transaction_id: '006', customer_name: 'Lisa Garcia', total_amount: 1650, category: 'Electronics', payment_method: 'PayPal', transaction_date: '2024-02-15', state: 'WA' },
|
|
45
|
+
{ transaction_id: '007', customer_name: 'Robert Lee', total_amount: 320, category: 'Books', payment_method: 'Cash', transaction_date: '2024-02-20', state: 'OR' },
|
|
46
|
+
{ transaction_id: '008', customer_name: 'Emily Davis', total_amount: 950, category: 'Clothing', payment_method: 'Credit Card', transaction_date: '2024-03-01', state: 'NY' },
|
|
47
|
+
{ transaction_id: '009', customer_name: 'Alex Rodriguez', total_amount: 1480, category: 'Sports', payment_method: 'Debit Card', transaction_date: '2024-03-05', state: 'TX' },
|
|
48
|
+
{ transaction_id: '010', customer_name: 'Maria Martinez', total_amount: 670, category: 'Beauty', payment_method: 'PayPal', transaction_date: '2024-03-08', state: 'FL' },
|
|
49
|
+
{ transaction_id: '011', customer_name: 'Chris Anderson', total_amount: 2200, category: 'Electronics', payment_method: 'Credit Card', transaction_date: '2024-03-12', state: 'CA' },
|
|
50
|
+
{ transaction_id: '012', customer_name: 'Amanda White', total_amount: 520, category: 'Clothing', payment_method: 'Debit Card', transaction_date: '2024-03-15', state: 'WA' },
|
|
51
|
+
{ transaction_id: '013', customer_name: 'James Taylor', total_amount: 1100, category: 'Sports', payment_method: 'Credit Card', transaction_date: '2024-03-18', state: 'OR' },
|
|
52
|
+
{ transaction_id: '014', customer_name: 'Jennifer Moore', total_amount: 380, category: 'Books', payment_method: 'Cash', transaction_date: '2024-03-22', state: 'NY' },
|
|
53
|
+
{ transaction_id: '015', customer_name: 'Kevin Jackson', total_amount: 1850, category: 'Electronics', payment_method: 'PayPal', transaction_date: '2024-03-25', state: 'TX' },
|
|
54
|
+
{ transaction_id: '016', customer_name: 'Nicole Thompson', total_amount: 760, category: 'Beauty', payment_method: 'Credit Card', transaction_date: '2024-04-01', state: 'FL' },
|
|
55
|
+
{ transaction_id: '017', customer_name: 'Brian Wilson', total_amount: 1320, category: 'Sports', payment_method: 'Debit Card', transaction_date: '2024-04-05', state: 'CA' },
|
|
56
|
+
{ transaction_id: '018', customer_name: 'Jessica Clark', total_amount: 590, category: 'Clothing', payment_method: 'Credit Card', transaction_date: '2024-04-08', state: 'WA' },
|
|
57
|
+
{ transaction_id: '019', customer_name: 'Michael Lewis', total_amount: 2400, category: 'Electronics', payment_method: 'PayPal', transaction_date: '2024-04-12', state: 'OR' },
|
|
58
|
+
{ transaction_id: '020', customer_name: 'Ashley Hall', total_amount: 450, category: 'Books', payment_method: 'Cash', transaction_date: '2024-04-15', state: 'NY' }
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
// Use real data if available, otherwise use sample data
|
|
62
|
+
const rawData = rawApiData.length > 0 ? rawApiData : sampleTransactionData
|
|
63
|
+
|
|
64
|
+
const orderData = processOrderData(rawData)
|
|
65
|
+
const paymentMethodData = processPaymentMethodData(rawData)
|
|
66
|
+
const monthlyRevenueData = processMonthlyRevenueData(rawData)
|
|
67
|
+
const recentTransactions = processRecentTransactions(rawData)
|
|
68
|
+
const { totalOrders, totalRevenue, averageOrderValue } = calculateMetrics(rawData)
|
|
69
|
+
const ecommerceMetrics = createEcommerceMetrics(totalRevenue, totalOrders, averageOrderValue)
|
|
70
|
+
|
|
71
|
+
// Helper function to render query state
|
|
72
|
+
const renderQueryState = (query: any, title: string) => {
|
|
73
|
+
const result = renderQueryStateUtil(query, title)
|
|
74
|
+
|
|
75
|
+
if (result.status === 'loading') {
|
|
76
|
+
return (
|
|
77
|
+
<div className="flex items-center gap-2 text-blue-600">
|
|
78
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
79
|
+
<span className="text-sm">{result.message}</span>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (result.status === 'error') {
|
|
85
|
+
return (
|
|
86
|
+
<div className="flex items-center gap-2 text-red-600">
|
|
87
|
+
<AlertCircle className="h-4 w-4" />
|
|
88
|
+
<span className="text-sm">{result.message}</span>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (result.status === 'success') {
|
|
94
|
+
return (
|
|
95
|
+
<div className="flex items-center gap-2 text-green-600">
|
|
96
|
+
<CheckCircle className="h-4 w-4" />
|
|
97
|
+
<span className="text-sm">{result.message}</span>
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="flex items-center gap-2 text-gray-500">
|
|
104
|
+
<Clock className="h-4 w-4" />
|
|
105
|
+
<span className="text-sm">{result.message}</span>
|
|
106
|
+
</div>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<div className="space-y-6">
|
|
112
|
+
{/* API Status */}
|
|
113
|
+
<div className="flex items-center justify-between">
|
|
114
|
+
<div className="flex items-center space-x-4">
|
|
115
|
+
{renderQueryState(metricQuery, 'Metrics API')}
|
|
116
|
+
{renderQueryState(datasetQuery, 'Dataset API')}
|
|
117
|
+
{renderQueryState(userQuery, 'User API')}
|
|
118
|
+
</div>
|
|
119
|
+
<Button
|
|
120
|
+
variant="outline"
|
|
121
|
+
size="sm"
|
|
122
|
+
onClick={() => {
|
|
123
|
+
metricQuery.refetch()
|
|
124
|
+
datasetQuery.refetch()
|
|
125
|
+
userQuery.refetch()
|
|
126
|
+
}}
|
|
127
|
+
disabled={metricQuery.isLoading || datasetQuery.isLoading || userQuery.isLoading ||
|
|
128
|
+
metricQuery.isFetching || datasetQuery.isFetching || userQuery.isFetching}
|
|
129
|
+
>
|
|
130
|
+
{(metricQuery.isFetching || datasetQuery.isFetching || userQuery.isFetching) ? (
|
|
131
|
+
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
132
|
+
) : (
|
|
133
|
+
<RefreshCw className="h-4 w-4 mr-2" />
|
|
134
|
+
)}
|
|
135
|
+
Refresh Data
|
|
136
|
+
</Button>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* Key Metrics */}
|
|
140
|
+
<DashboardMetrics
|
|
141
|
+
ecommerceMetrics={ecommerceMetrics}
|
|
142
|
+
monthlyRevenueData={monthlyRevenueData}
|
|
143
|
+
/>
|
|
144
|
+
|
|
145
|
+
{/* Sales Analytics */}
|
|
146
|
+
<SalesAnalytics
|
|
147
|
+
orderData={orderData}
|
|
148
|
+
totalOrders={totalOrders}
|
|
149
|
+
totalRevenue={totalRevenue}
|
|
150
|
+
averageOrderValue={averageOrderValue}
|
|
151
|
+
/>
|
|
152
|
+
|
|
153
|
+
{/* Payment Methods Analysis */}
|
|
154
|
+
<PaymentMethodsAnalysis
|
|
155
|
+
paymentMethodData={paymentMethodData}
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
{/* Recent Transactions */}
|
|
159
|
+
<TransactionsTable
|
|
160
|
+
recentTransactions={recentTransactions}
|
|
161
|
+
totalOrders={totalOrders}
|
|
162
|
+
/>
|
|
163
|
+
|
|
164
|
+
{/* Revenue Analytics */}
|
|
165
|
+
<RevenueAnalytics
|
|
166
|
+
orderData={orderData}
|
|
167
|
+
monthlyRevenueData={monthlyRevenueData}
|
|
168
|
+
totalOrders={totalOrders}
|
|
169
|
+
totalRevenue={totalRevenue}
|
|
170
|
+
averageOrderValue={averageOrderValue}
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
{/* Data Source Info */}
|
|
174
|
+
{userQuery.data?.user && (
|
|
175
|
+
<Alert>
|
|
176
|
+
<AlertCircle className="h-4 w-4" />
|
|
177
|
+
<AlertDescription>
|
|
178
|
+
Data is being fetched from Copa-lib APIs. Current user: {userQuery.data.user.name}
|
|
179
|
+
({userQuery.data.user.organization || 'No organization'})
|
|
180
|
+
</AlertDescription>
|
|
181
|
+
</Alert>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { createRoot, Root } from
|
|
3
|
-
import singleSpaReact from
|
|
4
|
-
import { MemoryRouter, BrowserRouter } from
|
|
5
|
-
import App from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createRoot, Root } from "react-dom/client";
|
|
3
|
+
import singleSpaReact from "single-spa-react";
|
|
4
|
+
import { MemoryRouter, BrowserRouter } from "react-router-dom";
|
|
5
|
+
import App from "./App";
|
|
6
|
+
import { AppProps } from "single-spa";
|
|
6
7
|
|
|
7
8
|
// Single-spa lifecycle props interface
|
|
8
9
|
interface LifecycleProps {
|
|
@@ -18,15 +19,15 @@ interface LifecycleProps {
|
|
|
18
19
|
let root: Root | null = null;
|
|
19
20
|
|
|
20
21
|
// Root component wrapper that handles routing
|
|
21
|
-
const
|
|
22
|
-
basename
|
|
23
|
-
isMicroFrontend
|
|
24
|
-
}) => {
|
|
22
|
+
const MicrofrontendRoot: React.FC<{
|
|
23
|
+
basename?: string;
|
|
24
|
+
isMicroFrontend?: boolean;
|
|
25
|
+
}> = ({ basename = "/", isMicroFrontend = false }) => {
|
|
25
26
|
// Use MemoryRouter for micro-frontend to avoid conflicts with host routing
|
|
26
27
|
// Use BrowserRouter for standalone mode
|
|
27
28
|
const Router = isMicroFrontend ? MemoryRouter : BrowserRouter;
|
|
28
29
|
const routerProps = isMicroFrontend ? {} : { basename };
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
return (
|
|
31
32
|
<Router {...routerProps}>
|
|
32
33
|
<App />
|
|
@@ -38,13 +39,22 @@ const PnLExplorerRoot: React.FC<{ basename?: string; isMicroFrontend?: boolean }
|
|
|
38
39
|
const lifecycles = singleSpaReact({
|
|
39
40
|
React,
|
|
40
41
|
ReactDOMClient: { createRoot },
|
|
41
|
-
rootComponent: (props
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
rootComponent: (props: AppProps & { basename: string }) => (
|
|
43
|
+
<MicrofrontendRoot isMicroFrontend={true} basename={props.basename} />
|
|
44
|
+
),
|
|
45
|
+
errorBoundary: (err, info) => {
|
|
46
|
+
console.error("Microfrontend Single-spa Error:", err, info);
|
|
47
|
+
return <div>Something went wrong loading Microfrontend</div>;
|
|
48
|
+
},
|
|
49
|
+
renderType: "createRoot",
|
|
50
|
+
domElementGetter: () => {
|
|
51
|
+
const el = document.getElementById("single-spa-application:pnl-explorer");
|
|
52
|
+
if (!el)
|
|
53
|
+
throw new Error(
|
|
54
|
+
"Mount target #single-spa-application:pnl-explorer not found"
|
|
55
|
+
);
|
|
56
|
+
return el;
|
|
45
57
|
},
|
|
46
|
-
renderType: 'createRoot',
|
|
47
|
-
domElementGetter: () => document.getElementById('single-spa-application:pnl-explorer') || document.body
|
|
48
58
|
});
|
|
49
59
|
|
|
50
60
|
// Export the single-spa lifecycle functions
|
|
@@ -53,7 +63,7 @@ export const { mount, unmount, bootstrap } = lifecycles;
|
|
|
53
63
|
// Export a manual mount function for direct usage
|
|
54
64
|
export const manualMount = async (props: LifecycleProps) => {
|
|
55
65
|
if (!props.domElement) {
|
|
56
|
-
throw new Error(
|
|
66
|
+
throw new Error("domElement is required for mounting the application");
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
try {
|
|
@@ -65,20 +75,20 @@ export const manualMount = async (props: LifecycleProps) => {
|
|
|
65
75
|
|
|
66
76
|
// Create new root
|
|
67
77
|
root = createRoot(props.domElement);
|
|
68
|
-
|
|
78
|
+
|
|
69
79
|
// Mount the application with the provided basename as micro-frontend
|
|
70
80
|
root.render(
|
|
71
|
-
<
|
|
81
|
+
<MicrofrontendRoot
|
|
72
82
|
isMicroFrontend={true}
|
|
73
|
-
basename={props.basename ||
|
|
83
|
+
basename={props.basename || "/app/external/microfrontend"}
|
|
74
84
|
/>
|
|
75
85
|
);
|
|
76
86
|
|
|
77
|
-
console.log(
|
|
78
|
-
return
|
|
87
|
+
console.log("Microfrontend mounted successfully");
|
|
88
|
+
return;
|
|
79
89
|
} catch (error) {
|
|
80
|
-
console.error(
|
|
81
|
-
|
|
90
|
+
console.error("Failed to mount Microfrontend:", error);
|
|
91
|
+
throw error;
|
|
82
92
|
}
|
|
83
93
|
};
|
|
84
94
|
|
|
@@ -89,10 +99,10 @@ export const manualUnmount = async () => {
|
|
|
89
99
|
root.unmount();
|
|
90
100
|
root = null;
|
|
91
101
|
}
|
|
92
|
-
console.log(
|
|
102
|
+
console.log("Microfrontend unmounted successfully");
|
|
93
103
|
return Promise.resolve();
|
|
94
104
|
} catch (error) {
|
|
95
|
-
console.error(
|
|
105
|
+
console.error("Failed to unmount Microfrontend:", error);
|
|
96
106
|
return Promise.reject(error);
|
|
97
107
|
}
|
|
98
108
|
};
|
|
@@ -101,5 +111,5 @@ export const manualUnmount = async () => {
|
|
|
101
111
|
export default {
|
|
102
112
|
mount: manualMount,
|
|
103
113
|
unmount: manualUnmount,
|
|
104
|
-
bootstrap: () => Promise.resolve()
|
|
105
|
-
};
|
|
114
|
+
bootstrap: () => Promise.resolve(),
|
|
115
|
+
};
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"paths": {
|
|
10
10
|
"@/*": ["./src/*"]
|
|
11
11
|
},
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"noUnusedLocals":
|
|
17
|
-
"
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"strictNullChecks": true,
|
|
15
|
+
"noUnusedParameters": true,
|
|
16
|
+
"noUnusedLocals": true,
|
|
17
|
+
"skipLibCheck": true
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
"skipLibCheck": true,
|
|
5
5
|
"module": "ESNext",
|
|
6
6
|
"moduleResolution": "bundler",
|
|
7
|
-
"allowSyntheticDefaultImports": true
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"baseUrl": ".",
|
|
9
|
+
"paths": {
|
|
10
|
+
"@/*": ["./src/*"]
|
|
11
|
+
}
|
|
8
12
|
},
|
|
9
13
|
"include": ["vite.config.ts"]
|
|
10
14
|
}
|