mcpengine/servers/xero/bulk-generate.py

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!")