- 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
172 lines
7.5 KiB
TypeScript
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>`;
|