- 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
164 lines
6.7 KiB
TypeScript
164 lines
6.7 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>Availability Calendar</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: 900px; margin: 0 auto; }
|
|
h1 { color: #333; margin-bottom: 20px; }
|
|
.calendar-header { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
|
|
.calendar-header select { padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin-right: 10px; }
|
|
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; background: white; padding: 20px; border-radius: 8px; }
|
|
.day-header { text-align: center; font-weight: 600; color: #666; padding: 10px; font-size: 14px; }
|
|
.day-cell { aspect-ratio: 1; border: 2px solid #E5E7EB; border-radius: 8px; padding: 8px; cursor: pointer; transition: all 0.2s; position: relative; }
|
|
.day-cell:hover { border-color: #4F46E5; }
|
|
.day-cell.available { background: #D1FAE5; border-color: #10B981; }
|
|
.day-cell.limited { background: #FEF3C7; border-color: #F59E0B; }
|
|
.day-cell.unavailable { background: #F3F4F6; border-color: #D1D5DB; cursor: not-allowed; }
|
|
.day-cell.selected { border-color: #4F46E5; border-width: 3px; }
|
|
.day-number { font-weight: 600; font-size: 16px; margin-bottom: 4px; }
|
|
.slots-available { font-size: 11px; color: #666; }
|
|
.time-slots { background: white; padding: 20px; border-radius: 8px; margin-top: 20px; }
|
|
.time-slot { display: inline-block; padding: 8px 16px; margin: 5px; border: 1px solid #ddd; border-radius: 6px; cursor: pointer; background: white; }
|
|
.time-slot:hover { background: #4F46E5; color: white; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="text/babel">
|
|
const { useState, useEffect } = React;
|
|
|
|
function AvailabilityCalendar() {
|
|
const [selectedDate, setSelectedDate] = useState(null);
|
|
const [appointmentType, setAppointmentType] = useState('1');
|
|
const [calendar, setCalendar] = useState('all');
|
|
const [availabilityData, setAvailabilityData] = useState({});
|
|
const [timeSlots, setTimeSlots] = useState([]);
|
|
|
|
useEffect(() => {
|
|
fetchAvailability();
|
|
}, [appointmentType, calendar]);
|
|
|
|
useEffect(() => {
|
|
if (selectedDate) {
|
|
fetchTimeSlots(selectedDate);
|
|
}
|
|
}, [selectedDate]);
|
|
|
|
const fetchAvailability = async () => {
|
|
// Mock availability data
|
|
const mockData = {
|
|
'2024-01-15': { available: true, slots: 8 },
|
|
'2024-01-16': { available: true, slots: 5 },
|
|
'2024-01-17': { available: true, slots: 3 },
|
|
'2024-01-18': { available: true, slots: 10 },
|
|
'2024-01-19': { available: true, slots: 2 },
|
|
'2024-01-22': { available: true, slots: 6 },
|
|
'2024-01-23': { available: false, slots: 0 },
|
|
};
|
|
setAvailabilityData(mockData);
|
|
};
|
|
|
|
const fetchTimeSlots = async (date) => {
|
|
// Mock time slots
|
|
const mockSlots = [
|
|
'09:00 AM', '10:00 AM', '11:00 AM', '01:00 PM', '02:00 PM', '03:00 PM', '04:00 PM'
|
|
];
|
|
setTimeSlots(mockSlots);
|
|
};
|
|
|
|
const getDaysInMonth = () => {
|
|
const days = [];
|
|
const firstDay = new Date(2024, 0, 1);
|
|
const lastDay = new Date(2024, 0, 31);
|
|
|
|
for (let d = new Date(firstDay); d <= lastDay; d.setDate(d.getDate() + 1)) {
|
|
days.push(new Date(d));
|
|
}
|
|
return days;
|
|
};
|
|
|
|
const getDayClass = (date) => {
|
|
const dateStr = date.toISOString().split('T')[0];
|
|
const availability = availabilityData[dateStr];
|
|
|
|
if (!availability) return 'unavailable';
|
|
if (availability.slots > 5) return 'available';
|
|
if (availability.slots > 0) return 'limited';
|
|
return 'unavailable';
|
|
};
|
|
|
|
const handleDateClick = (date) => {
|
|
const dateStr = date.toISOString().split('T')[0];
|
|
const availability = availabilityData[dateStr];
|
|
if (availability && availability.slots > 0) {
|
|
setSelectedDate(dateStr);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="container">
|
|
<h1>Availability Calendar</h1>
|
|
|
|
<div className="calendar-header">
|
|
<select value={appointmentType} onChange={(e) => setAppointmentType(e.target.value)}>
|
|
<option value="1">Initial Consultation</option>
|
|
<option value="2">Follow-up Appointment</option>
|
|
<option value="3">Assessment</option>
|
|
</select>
|
|
<select value={calendar} onChange={(e) => setCalendar(e.target.value)}>
|
|
<option value="all">All Calendars</option>
|
|
<option value="dr-smith">Dr. Smith</option>
|
|
<option value="dr-johnson">Dr. Johnson</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="calendar-grid">
|
|
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
|
|
<div key={day} className="day-header">{day}</div>
|
|
))}
|
|
|
|
{getDaysInMonth().map((date, idx) => {
|
|
const dateStr = date.toISOString().split('T')[0];
|
|
const availability = availabilityData[dateStr];
|
|
const isSelected = selectedDate === dateStr;
|
|
|
|
return (
|
|
<div
|
|
key={idx}
|
|
className={\`day-cell \${getDayClass(date)} \${isSelected ? 'selected' : ''}\`}
|
|
onClick={() => handleDateClick(date)}
|
|
>
|
|
<div className="day-number">{date.getDate()}</div>
|
|
{availability && availability.slots > 0 && (
|
|
<div className="slots-available">{availability.slots} slots</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{selectedDate && timeSlots.length > 0 && (
|
|
<div className="time-slots">
|
|
<h2>Available Times for {selectedDate}</h2>
|
|
<div>
|
|
{timeSlots.map(slot => (
|
|
<button key={slot} className="time-slot">{slot}</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.render(<AvailabilityCalendar />, document.getElementById('root'));
|
|
</script>
|
|
</body>
|
|
</html>`;
|