- Full API client with Basic Auth and OAuth2 support - 40+ tools across 10 categories (appointments, availability, clients, calendars, products, forms, labels, webhooks, coupons, blocks) - 14 interactive React-based MCP apps for rich UI experiences - Comprehensive error handling and pagination - TypeScript implementation with full type definitions - Complete documentation and examples
185 lines
6.9 KiB
TypeScript
185 lines
6.9 KiB
TypeScript
export default `<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Coupon Manager</title>
|
|
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
<style>
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
h1 { color: #333; margin-bottom: 20px; }
|
|
.toolbar { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; display: flex; justify-content: space-between; }
|
|
button { padding: 10px 20px; background: #4F46E5; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; }
|
|
button:hover { background: #4338CA; }
|
|
.coupons-list { background: white; border-radius: 8px; overflow: hidden; }
|
|
.coupon-item { padding: 20px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
|
|
.coupon-item:last-child { border-bottom: none; }
|
|
.coupon-main { flex: 1; }
|
|
.coupon-code { font-size: 20px; font-weight: 700; color: #4F46E5; font-family: monospace; margin-bottom: 5px; }
|
|
.coupon-details { font-size: 14px; color: #666; }
|
|
.coupon-stats { display: flex; gap: 20px; margin-top: 10px; }
|
|
.stat { font-size: 13px; }
|
|
.stat-label { color: #666; }
|
|
.stat-value { font-weight: 600; color: #333; }
|
|
.coupon-status { padding: 6px 12px; border-radius: 12px; font-size: 12px; font-weight: 500; }
|
|
.status-active { background: #D1FAE5; color: #065F46; }
|
|
.status-expired { background: #FEE2E2; color: #991B1B; }
|
|
.status-maxed { background: #F3F4F6; color: #6B7280; }
|
|
.coupon-actions { display: flex; gap: 10px; }
|
|
.btn-small { padding: 6px 12px; font-size: 13px; }
|
|
.btn-secondary { background: #6B7280; }
|
|
.btn-secondary:hover { background: #4B5563; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="text/babel">
|
|
const { useState, useEffect } = React;
|
|
|
|
function CouponManager() {
|
|
const [coupons, setCoupons] = useState([]);
|
|
|
|
useEffect(() => {
|
|
fetchCoupons();
|
|
}, []);
|
|
|
|
const fetchCoupons = async () => {
|
|
// Mock data
|
|
const mockCoupons = [
|
|
{
|
|
id: 1,
|
|
code: 'WELCOME20',
|
|
name: 'New Client Welcome',
|
|
percentageOff: 20,
|
|
validFrom: '2024-01-01',
|
|
validTo: '2024-12-31',
|
|
maxUses: 100,
|
|
timesUsed: 23,
|
|
status: 'active'
|
|
},
|
|
{
|
|
id: 2,
|
|
code: 'SUMMER50',
|
|
name: 'Summer Special',
|
|
amountOff: 50,
|
|
validFrom: '2024-06-01',
|
|
validTo: '2024-08-31',
|
|
maxUses: 50,
|
|
timesUsed: 12,
|
|
status: 'active'
|
|
},
|
|
{
|
|
id: 3,
|
|
code: 'HOLIDAY2023',
|
|
name: 'Holiday Promotion',
|
|
percentageOff: 30,
|
|
validFrom: '2023-11-01',
|
|
validTo: '2023-12-31',
|
|
maxUses: 200,
|
|
timesUsed: 156,
|
|
status: 'expired'
|
|
},
|
|
{
|
|
id: 4,
|
|
code: 'VIP10',
|
|
name: 'VIP Discount',
|
|
percentageOff: 10,
|
|
validFrom: '2024-01-01',
|
|
validTo: '2024-12-31',
|
|
maxUses: 10,
|
|
timesUsed: 10,
|
|
status: 'maxed'
|
|
},
|
|
];
|
|
setCoupons(mockCoupons);
|
|
};
|
|
|
|
const handleAddCoupon = () => {
|
|
alert('Create new coupon - would open form');
|
|
};
|
|
|
|
const handleEditCoupon = (coupon) => {
|
|
alert(\`Edit coupon: \${coupon.code}\`);
|
|
};
|
|
|
|
const handleDeleteCoupon = (coupon) => {
|
|
if (confirm(\`Delete coupon "\${coupon.code}"?\`)) {
|
|
setCoupons(coupons.filter(c => c.id !== coupon.id));
|
|
}
|
|
};
|
|
|
|
const getDiscountText = (coupon) => {
|
|
if (coupon.percentageOff) return \`\${coupon.percentageOff}% off\`;
|
|
if (coupon.amountOff) return \`$\${coupon.amountOff} off\`;
|
|
return 'Discount';
|
|
};
|
|
|
|
const getStatusClass = (status) => {
|
|
if (status === 'active') return 'status-active';
|
|
if (status === 'expired') return 'status-expired';
|
|
if (status === 'maxed') return 'status-maxed';
|
|
return '';
|
|
};
|
|
|
|
const getStatusText = (status) => {
|
|
if (status === 'active') return 'Active';
|
|
if (status === 'expired') return 'Expired';
|
|
if (status === 'maxed') return 'Max Uses Reached';
|
|
return status;
|
|
};
|
|
|
|
return (
|
|
<div className="container">
|
|
<h1>Coupon Manager</h1>
|
|
|
|
<div className="toolbar">
|
|
<div style={{ color: '#666' }}>
|
|
{coupons.length} coupon{coupons.length !== 1 ? 's' : ''}
|
|
</div>
|
|
<button onClick={handleAddCoupon}>+ Create Coupon</button>
|
|
</div>
|
|
|
|
<div className="coupons-list">
|
|
{coupons.map(coupon => (
|
|
<div key={coupon.id} className="coupon-item">
|
|
<div className="coupon-main">
|
|
<div className="coupon-code">{coupon.code}</div>
|
|
<div className="coupon-details">
|
|
{coupon.name} • {getDiscountText(coupon)} •
|
|
Valid {new Date(coupon.validFrom).toLocaleDateString()} - {new Date(coupon.validTo).toLocaleDateString()}
|
|
</div>
|
|
<div className="coupon-stats">
|
|
<div className="stat">
|
|
<span className="stat-label">Used: </span>
|
|
<span className="stat-value">{coupon.timesUsed}/{coupon.maxUses}</span>
|
|
</div>
|
|
<div className="stat">
|
|
<span className="stat-label">Remaining: </span>
|
|
<span className="stat-value">{coupon.maxUses - coupon.timesUsed}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
|
|
<span className={\`coupon-status \${getStatusClass(coupon.status)}\`}>
|
|
{getStatusText(coupon.status)}
|
|
</span>
|
|
<div className="coupon-actions">
|
|
<button className="btn-small" onClick={() => handleEditCoupon(coupon)}>Edit</button>
|
|
<button className="btn-small btn-secondary" onClick={() => handleDeleteCoupon(coupon)}>Delete</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.render(<CouponManager />, document.getElementById('root'));
|
|
</script>
|
|
</body>
|
|
</html>`;
|