Jake Shore f8e0b3246f feat: Complete Acuity Scheduling MCP server with 40+ tools and 14 React apps
- 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
2026-02-12 17:41:55 -05:00

172 lines
7.5 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>Schedule Overview</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: 1400px; margin: 0 auto; }
h1 { color: #333; margin-bottom: 20px; }
.controls { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; }
.date-nav { display: flex; gap: 10px; align-items: center; }
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; }
.btn-secondary { background: #6B7280; }
.btn-secondary:hover { background: #4B5563; }
.schedule-grid { background: white; border-radius: 8px; overflow: hidden; }
.calendar-header { display: grid; grid-template-columns: 100px repeat(7, 1fr); background: #F9FAFB; border-bottom: 2px solid #E5E7EB; }
.header-cell { padding: 15px; text-align: center; font-weight: 600; color: #374151; }
.time-row { display: grid; grid-template-columns: 100px repeat(7, 1fr); border-bottom: 1px solid #E5E7EB; min-height: 60px; }
.time-cell { padding: 10px; color: #666; font-size: 14px; background: #F9FAFB; border-right: 1px solid #E5E7EB; }
.appointment-cell { padding: 5px; border-right: 1px solid #E5E7EB; position: relative; }
.appointment-block { background: #EEF2FF; border-left: 3px solid #4F46E5; padding: 8px; margin: 2px; border-radius: 4px; cursor: pointer; font-size: 13px; }
.appointment-block:hover { background: #E0E7FF; }
.appointment-time { font-weight: 600; color: #4F46E5; }
.appointment-client { color: #333; margin-top: 2px; }
.appointment-type { color: #666; font-size: 11px; }
.stats-bar { display: flex; gap: 20px; padding: 15px 20px; background: #F9FAFB; border-top: 1px solid #E5E7EB; }
.stat { font-size: 13px; color: #666; }
.stat strong { color: #4F46E5; font-weight: 600; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect } = React;
function ScheduleOverview() {
const [currentWeek, setCurrentWeek] = useState(new Date());
const [appointments, setAppointments] = useState([]);
const [calendars, setCalendars] = useState([]);
useEffect(() => {
fetchScheduleData();
}, [currentWeek]);
const fetchScheduleData = async () => {
// Mock data
const mockCalendars = ['Dr. Smith', 'Dr. Johnson', 'Dr. Davis'];
const mockAppointments = [
{ id: 1, calendar: 'Dr. Smith', day: 1, time: '09:00', client: 'John Doe', type: 'Consultation' },
{ id: 2, calendar: 'Dr. Smith', day: 1, time: '10:30', client: 'Jane Smith', type: 'Follow-up' },
{ id: 3, calendar: 'Dr. Johnson', day: 1, time: '09:00', client: 'Bob Wilson', type: 'Assessment' },
{ id: 4, calendar: 'Dr. Smith', day: 2, time: '14:00', client: 'Alice Brown', type: 'Consultation' },
{ id: 5, calendar: 'Dr. Davis', day: 3, time: '11:00', client: 'Charlie Davis', type: 'Follow-up' },
];
setCalendars(mockCalendars);
setAppointments(mockAppointments);
};
const getDaysOfWeek = () => {
const days = [];
const startOfWeek = new Date(currentWeek);
startOfWeek.setDate(currentWeek.getDate() - currentWeek.getDay());
for (let i = 0; i < 7; i++) {
const day = new Date(startOfWeek);
day.setDate(startOfWeek.getDate() + i);
days.push(day);
}
return days;
};
const timeSlots = ['09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00'];
const getAppointmentsForSlot = (day, time) => {
return appointments.filter(apt =>
apt.day === day && apt.time === time
);
};
const handlePrevWeek = () => {
const newDate = new Date(currentWeek);
newDate.setDate(currentWeek.getDate() - 7);
setCurrentWeek(newDate);
};
const handleNextWeek = () => {
const newDate = new Date(currentWeek);
newDate.setDate(currentWeek.getDate() + 7);
setCurrentWeek(newDate);
};
const handleToday = () => {
setCurrentWeek(new Date());
};
const daysOfWeek = getDaysOfWeek();
const totalAppointments = appointments.length;
return (
<div className="container">
<h1>Schedule Overview</h1>
<div className="controls">
<div className="date-nav">
<button className="btn-secondary" onClick={handlePrevWeek}>← Previous</button>
<button className="btn-secondary" onClick={handleToday}>Today</button>
<button className="btn-secondary" onClick={handleNextWeek}>Next →</button>
<span style={{ marginLeft: '20px', fontSize: '16px', fontWeight: 600 }}>
Week of {daysOfWeek[0].toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
</span>
</div>
<select style={{ padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}>
<option>All Calendars</option>
{calendars.map(cal => <option key={cal}>{cal}</option>)}
</select>
</div>
<div className="schedule-grid">
<div className="calendar-header">
<div className="header-cell">Time</div>
{daysOfWeek.map((day, idx) => (
<div key={idx} className="header-cell">
{day.toLocaleDateString('en-US', { weekday: 'short', month: 'numeric', day: 'numeric' })}
</div>
))}
</div>
{timeSlots.map(time => (
<div key={time} className="time-row">
<div className="time-cell">{time}</div>
{daysOfWeek.map((_, dayIdx) => {
const apts = getAppointmentsForSlot(dayIdx, time);
return (
<div key={dayIdx} className="appointment-cell">
{apts.map(apt => (
<div key={apt.id} className="appointment-block">
<div className="appointment-time">{apt.time}</div>
<div className="appointment-client">{apt.client}</div>
<div className="appointment-type">{apt.type}</div>
</div>
))}
</div>
);
})}
</div>
))}
<div className="stats-bar">
<div className="stat">
<strong>{totalAppointments}</strong> appointments this week
</div>
<div className="stat">
<strong>{calendars.length}</strong> active calendars
</div>
</div>
</div>
</div>
);
}
ReactDOM.render(<ScheduleOverview />, document.getElementById('root'));
</script>
</body>
</html>`;