186 lines
8.1 KiB
Python
186 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
|
|
BASE_DIR = "/Users/jakeshore/.clawdbot/workspace/mcpengine-repo/servers/xero/src/apps"
|
|
|
|
# Define remaining apps with their specific data
|
|
apps = [
|
|
{
|
|
"name": "bank-feed",
|
|
"title": "Bank Feed",
|
|
"subtitle": "Bank transactions and reconciliation",
|
|
"icon": "🏦",
|
|
"mock_type": "BankTransaction",
|
|
"mock_data": [
|
|
"{ id: '1', date: '2024-01-15', description: 'Client Payment - INV-001', type: 'RECEIVE', amount: 12500.00, status: 'MATCHED' }",
|
|
"{ id: '2', date: '2024-01-16', description: 'Office Supplies', type: 'SPEND', amount: -350.00, status: 'UNMATCHED' }",
|
|
"{ id: '3', date: '2024-01-17', description: 'Monthly Rent', type: 'SPEND', amount: -2500.00, status: 'MATCHED' }",
|
|
"{ id: '4', date: '2024-01-18', description: 'Client Payment - INV-002', type: 'RECEIVE', amount: 8750.00, status: 'PENDING' }",
|
|
"{ id: '5', date: '2024-01-20', description: 'Utility Bill', type: 'SPEND', amount: -450.00, status: 'UNMATCHED' }",
|
|
],
|
|
"columns": ["Date", "Description", "Type", "Amount", "Status"],
|
|
"stats": ["Total Transactions", "Matched", "Unmatched", "Pending"]
|
|
},
|
|
{
|
|
"name": "payment-tracker",
|
|
"title": "Payment Tracker",
|
|
"subtitle": "Track payments, prepayments, and overpayments",
|
|
"icon": "💳",
|
|
"mock_type": "Payment",
|
|
"mock_data": [
|
|
"{ id: '1', date: '2024-01-15', invoice: 'INV-0001', contact: 'Acme Corp', type: 'PAYMENT', amount: 12500.00, status: 'AUTHORISED' }",
|
|
"{ id: '2', date: '2024-01-18', invoice: 'INV-0002', contact: 'Tech Solutions', type: 'PREPAYMENT', amount: 5000.00, status: 'AUTHORISED' }",
|
|
"{ id: '3', date: '2024-01-20', invoice: 'INV-0003', contact: 'Global Services', type: 'OVERPAYMENT', amount: 1200.00, status: 'PENDING' }",
|
|
"{ id: '4', date: '2024-01-22', invoice: 'BILL-0001', contact: 'Office Supplies', type: 'PAYMENT', amount: 3500.00, status: 'AUTHORISED' }",
|
|
"{ id: '5', date: '2024-01-25', invoice: 'INV-0004', contact: 'StartUp Co', type: 'PAYMENT', amount: 5200.00, status: 'PENDING' }",
|
|
],
|
|
"columns": ["Date", "Invoice", "Contact", "Type", "Amount", "Status"],
|
|
"stats": ["Total Payments", "Authorised", "Pending", "Prepayments"]
|
|
},
|
|
{
|
|
"name": "credit-note-manager",
|
|
"title": "Credit Note Manager",
|
|
"subtitle": "Manage credit notes and allocations",
|
|
"icon": "📝",
|
|
"mock_type": "CreditNote",
|
|
"mock_data": [
|
|
"{ id: '1', creditNoteNumber: 'CN-0001', contact: 'Acme Corp', date: '2024-01-10', total: 1250.00, remaining: 1250.00, status: 'AUTHORISED' }",
|
|
"{ id: '2', creditNoteNumber: 'CN-0002', contact: 'Tech Solutions', date: '2024-01-12', total: 750.00, remaining: 0, status: 'PAID' }",
|
|
"{ id: '3', creditNoteNumber: 'CN-0003', contact: 'Global Services', date: '2024-01-15', total: 2200.00, remaining: 2200.00, status: 'DRAFT' }",
|
|
"{ id: '4', creditNoteNumber: 'CN-0004', contact: 'StartUp Co', date: '2024-01-18', total: 500.00, remaining: 500.00, status: 'SUBMITTED' }",
|
|
"{ id: '5', creditNoteNumber: 'CN-0005', contact: 'Enterprise LLC', date: '2024-01-20', total: 3100.00, remaining: 1500.00, status: 'AUTHORISED' }",
|
|
],
|
|
"columns": ["Credit Note #", "Contact", "Date", "Total", "Remaining", "Status"],
|
|
"stats": ["Total Credit Notes", "Outstanding", "Allocated", "Draft"]
|
|
},
|
|
{
|
|
"name": "purchase-orders",
|
|
"title": "Purchase Orders",
|
|
"subtitle": "Track PO list, status, and approvals",
|
|
"icon": "🛒",
|
|
"mock_type": "PurchaseOrder",
|
|
"mock_data": [
|
|
"{ id: '1', poNumber: 'PO-0001', supplier: 'Office Supplies Co', date: '2024-01-10', total: 3500.00, status: 'AUTHORISED' }",
|
|
"{ id: '2', poNumber: 'PO-0002', supplier: 'Tech Vendor', date: '2024-01-12', total: 8900.00, status: 'BILLED' }",
|
|
"{ id: '3', poNumber: 'PO-0003', supplier: 'Marketing Agency', date: '2024-01-15', total: 6750.00, status: 'DRAFT' }",
|
|
"{ id: '4', poNumber: 'PO-0004', supplier: 'Consulting Group', date: '2024-01-18', total: 15000.00, status: 'SUBMITTED' }",
|
|
"{ id: '5', poNumber: 'PO-0005', supplier: 'Equipment Rentals', date: '2024-01-20', total: 4200.00, status: 'AUTHORISED' }",
|
|
],
|
|
"columns": ["PO Number", "Supplier", "Date", "Total", "Status"],
|
|
"stats": ["Total POs", "Authorised", "Billed", "Draft"]
|
|
},
|
|
{
|
|
"name": "quote-builder",
|
|
"title": "Quote Builder",
|
|
"subtitle": "Manage quotes and convert to invoices",
|
|
"icon": "💼",
|
|
"mock_type": "Quote",
|
|
"mock_data": [
|
|
"{ id: '1', quoteNumber: 'QT-0001', contact: 'Acme Corp', date: '2024-01-10', expiryDate: '2024-02-10', total: 15000.00, status: 'DRAFT' }",
|
|
"{ id: '2', quoteNumber: 'QT-0002', contact: 'Tech Solutions', date: '2024-01-12', expiryDate: '2024-02-12', total: 8750.00, status: 'SENT' }",
|
|
"{ id: '3', quoteNumber: 'QT-0003', contact: 'Global Services', date: '2024-01-15', expiryDate: '2024-02-15', total: 22100.00, status: 'ACCEPTED' }",
|
|
"{ id: '4', quoteNumber: 'QT-0004', contact: 'StartUp Co', date: '2024-01-18', expiryDate: '2024-02-18', total: 5200.00, status: 'DECLINED' }",
|
|
"{ id: '5', quoteNumber: 'QT-0005', contact: 'Enterprise LLC', date: '2024-01-20', expiryDate: '2024-02-20', total: 18900.00, status: 'SENT' }",
|
|
],
|
|
"columns": ["Quote #", "Contact", "Date", "Expiry Date", "Total", "Status"],
|
|
"stats": ["Total Quotes", "Sent", "Accepted", "Draft"]
|
|
},
|
|
]
|
|
|
|
# Template files
|
|
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>
|
|
);
|
|
}
|
|
'''
|
|
|
|
# Generate files for each app
|
|
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 files for {app['name']}")
|
|
|
|
print("\n✨ Batch creation complete!")
|