158 lines
3.9 KiB
Python
158 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
|
|
BASE_DIR = "/Users/jakeshore/.clawdbot/workspace/mcpengine-repo/servers/xero/src/apps"
|
|
|
|
MAIN_TSX = '''import React, { Suspense, Component, ErrorInfo, ReactNode } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
|
|
const App = React.lazy(() => import('./App'));
|
|
|
|
interface ErrorBoundaryProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
interface ErrorBoundaryState {
|
|
hasError: boolean;
|
|
error?: Error;
|
|
}
|
|
|
|
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
constructor(props: ErrorBoundaryProps) {
|
|
super(props);
|
|
this.state = { hasError: false };
|
|
}
|
|
|
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
return { hasError: true, error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
console.error('Error caught by boundary:', error, errorInfo);
|
|
}
|
|
|
|
render() {
|
|
if (this.state.hasError) {
|
|
return (
|
|
<div style={{ padding: '2rem', textAlign: 'center' }}>
|
|
<h1>Something went wrong</h1>
|
|
<p>{this.state.error?.message}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return this.props.children;
|
|
}
|
|
}
|
|
|
|
const LoadingSkeleton = () => (
|
|
<div className="loading-skeleton">
|
|
<div className="shimmer" style={{ height: '60px', marginBottom: '1rem' }}></div>
|
|
<div className="shimmer" style={{ height: '120px', marginBottom: '1rem' }}></div>
|
|
<div className="shimmer" style={{ height: '400px' }}></div>
|
|
</div>
|
|
);
|
|
|
|
const container = document.getElementById('root');
|
|
if (container) {
|
|
const root = createRoot(container);
|
|
root.render(
|
|
<ErrorBoundary>
|
|
<Suspense fallback={<LoadingSkeleton />}>
|
|
<App />
|
|
</Suspense>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
'''
|
|
|
|
# Remaining 9 apps
|
|
apps = [
|
|
{
|
|
"name": "profit-loss",
|
|
"title": "Profit & Loss",
|
|
"subtitle": "P&L report viewer",
|
|
"icon": "📈",
|
|
},
|
|
{
|
|
"name": "balance-sheet",
|
|
"title": "Balance Sheet",
|
|
"subtitle": "Balance sheet viewer",
|
|
"icon": "⚖️",
|
|
},
|
|
{
|
|
"name": "trial-balance",
|
|
"title": "Trial Balance",
|
|
"subtitle": "Trial balance viewer",
|
|
"icon": "🧮",
|
|
},
|
|
{
|
|
"name": "aged-receivables",
|
|
"title": "Aged Receivables",
|
|
"subtitle": "AR aging report",
|
|
"icon": "📊",
|
|
},
|
|
{
|
|
"name": "aged-payables",
|
|
"title": "Aged Payables",
|
|
"subtitle": "AP aging report",
|
|
"icon": "📉",
|
|
},
|
|
{
|
|
"name": "employee-directory",
|
|
"title": "Employee Directory",
|
|
"subtitle": "Employee records",
|
|
"icon": "👥",
|
|
},
|
|
{
|
|
"name": "payroll-dashboard",
|
|
"title": "Payroll Dashboard",
|
|
"subtitle": "Pay runs, slips, and leave",
|
|
"icon": "💰",
|
|
},
|
|
{
|
|
"name": "tax-center",
|
|
"title": "Tax Center",
|
|
"subtitle": "Tax rates and returns",
|
|
"icon": "🏛️",
|
|
},
|
|
{
|
|
"name": "org-overview",
|
|
"title": "Organisation Overview",
|
|
"subtitle": "Organisation info, settings, and metrics",
|
|
"icon": "🏢",
|
|
},
|
|
]
|
|
|
|
for app in apps:
|
|
app_dir = os.path.join(BASE_DIR, app["name"])
|
|
|
|
# Create index.html
|
|
html_content = f'''<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{app["title"]} - Xero MCP</title>
|
|
<link rel="stylesheet" href="./styles.css">
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="./main.tsx"></script>
|
|
</body>
|
|
</html>
|
|
'''
|
|
with open(os.path.join(app_dir, "index.html"), "w") as f:
|
|
f.write(html_content)
|
|
|
|
# Create main.tsx
|
|
with open(os.path.join(app_dir, "main.tsx"), "w") as f:
|
|
f.write(MAIN_TSX)
|
|
|
|
# Copy styles.css from template
|
|
os.system(f"cp {BASE_DIR}/invoice-dashboard/styles.css {app_dir}/styles.css")
|
|
|
|
print(f"✅ Created {app['name']}")
|
|
|
|
print("\n✨ All files created!")
|