Skip to main content
This component is coming soon. This page documents the planned API and usage. Join the developer preview for early access.
The Availability Picker lets users visually set their weekly schedule with an intuitive drag-to-select interface.

Features

  • Drag-to-select - Click and drag to set available hours
  • Copy schedule - Duplicate one day’s schedule to others
  • Timezone support - Display in user’s local timezone
  • Presets - Quick-select common schedules
  • Date exceptions - Override availability for specific dates
  • Real-time sync - Changes saved automatically

Component Usage (Coming Soon)

Basic Embed

<div id="availability-picker"></div>

<script src="https://cdn.meetergo.com/components.js"></script>
<script>
  Meetergo.AvailabilityPicker({
    container: '#availability-picker',
    userId: 'user-uuid',
    availabilityId: 'availability-uuid',
    onSave: (availability) => {
      console.log('Saved:', availability);
    }
  });
</script>

React Component

import { AvailabilityPicker } from '@meetergo/react';

function AvailabilitySettings() {
  return (
    <AvailabilityPicker
      userId={userId}
      availabilityId={availabilityId}
      timezone="Europe/Berlin"
      format="24h"
      minInterval={30}
      onChange={(schedule) => {
        console.log('Changed:', schedule);
      }}
      onSave={(availability) => {
        toast.success('Availability saved!');
      }}
    />
  );
}

Configuration Options

Meetergo.AvailabilityPicker({
  // Required
  container: '#availability-picker',
  userId: 'user-uuid',

  // Optional - if not provided, uses user's default availability
  availabilityId: 'availability-uuid',

  // Display options
  timezone: 'Europe/Berlin',     // User's timezone
  format: '24h',                 // '24h' or '12h'
  weekStartsOn: 'monday',        // 'monday' or 'sunday'
  minInterval: 30,               // Minimum interval in minutes (15, 30, 60)
  dayStartHour: 6,               // First hour to show
  dayEndHour: 22,                // Last hour to show

  // Features
  showPresets: true,             // Show preset schedule buttons
  showCopyDay: true,             // Show "copy to other days" option
  showExceptions: true,          // Show date exception management
  autoSave: true,                // Save automatically on change
  autoSaveDelay: 1000,           // Debounce delay in ms

  // Styling
  theme: {
    primaryColor: '#2563eb',
    availableColor: '#22c55e',
    unavailableColor: '#f3f4f6',
    borderRadius: '8px'
  },

  // Labels (for i18n)
  labels: {
    title: 'Set your availability',
    save: 'Save',
    reset: 'Reset',
    copyDay: 'Copy to other days',
    presets: {
      businessHours: 'Business hours (9-17)',
      mornings: 'Mornings only',
      afternoons: 'Afternoons only',
      clear: 'Clear all'
    },
    days: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },

  // Callbacks
  onChange: (schedule) => {},    // Called on every change
  onSave: (availability) => {},  // Called when saved
  onError: (error) => {}
});

Schedule Data Format

The component works with this schedule format:
const schedule = [
  {
    dayOfWeek: 'monday',
    intervals: [
      { from: '09:00', to: '12:00' },
      { from: '13:00', to: '17:00' }
    ]
  },
  {
    dayOfWeek: 'tuesday',
    intervals: [
      { from: '09:00', to: '17:00' }
    ]
  },
  // ... other days
  {
    dayOfWeek: 'saturday',
    intervals: []  // Not available
  },
  {
    dayOfWeek: 'sunday',
    intervals: []
  }
];

Current API Implementation

While the component is in development, use the API:

Get Availability

async function getAvailability(userId, availabilityId) {
  const response = await fetch(
    `https://api.meetergo.com/availability/${availabilityId}?userId=${userId}`,
    {
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'x-meetergo-api-user-id': userId
      }
    }
  );

  return response.json();
}

Update Availability

async function updateAvailability(userId, availabilityId, schedule) {
  const response = await fetch(
    `https://api.meetergo.com/availability/${availabilityId}`,
    {
      method: 'PATCH',
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json',
        'x-meetergo-api-user-id': userId
      },
      body: JSON.stringify({ schedule })
    }
  );

  return response.json();
}

// Usage
await updateAvailability(userId, availabilityId, [
  {
    dayOfWeek: 'monday',
    intervals: [{ from: '09:00', to: '17:00' }]
  },
  {
    dayOfWeek: 'tuesday',
    intervals: [{ from: '09:00', to: '17:00' }]
  }
  // ... etc
]);

Add Date Exception

async function addException(userId, startDate, endDate, isAvailable, name) {
  const response = await fetch(
    'https://api.meetergo.com/availability-exception/user',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json',
        'x-meetergo-api-user-id': userId
      },
      body: JSON.stringify({
        startDate,
        endDate,
        isAvailable,
        name
      })
    }
  );

  return response.json();
}

// Block off vacation
await addException(userId, '2024-12-24', '2024-12-26', false, 'Christmas');

// Add extra availability
await addException(userId, '2024-12-28', '2024-12-28', true, 'Saturday coverage');

Building Your Own UI

Example React implementation:
import { useState, useEffect } from 'react';

const DAYS = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
const HOURS = Array.from({ length: 24 }, (_, i) => i);

function AvailabilityPickerCustom({ userId, availabilityId }) {
  const [schedule, setSchedule] = useState({});
  const [isDragging, setIsDragging] = useState(false);
  const [dragValue, setDragValue] = useState(true);

  useEffect(() => {
    loadAvailability();
  }, [userId, availabilityId]);

  async function loadAvailability() {
    const response = await fetch(`/api/availability/${availabilityId}`);
    const data = await response.json();

    // Convert to grid format
    const grid = {};
    DAYS.forEach(day => {
      grid[day] = {};
      HOURS.forEach(hour => {
        grid[day][hour] = false;
      });
    });

    data.schedule.forEach(daySchedule => {
      daySchedule.intervals.forEach(interval => {
        const startHour = parseInt(interval.from.split(':')[0]);
        const endHour = parseInt(interval.to.split(':')[0]);
        for (let h = startHour; h < endHour; h++) {
          grid[daySchedule.dayOfWeek][h] = true;
        }
      });
    });

    setSchedule(grid);
  }

  function handleMouseDown(day, hour) {
    setIsDragging(true);
    setDragValue(!schedule[day][hour]);
    toggleHour(day, hour);
  }

  function handleMouseEnter(day, hour) {
    if (isDragging) {
      setSchedule(prev => ({
        ...prev,
        [day]: { ...prev[day], [hour]: dragValue }
      }));
    }
  }

  function handleMouseUp() {
    setIsDragging(false);
    saveSchedule();
  }

  function toggleHour(day, hour) {
    setSchedule(prev => ({
      ...prev,
      [day]: { ...prev[day], [hour]: !prev[day][hour] }
    }));
  }

  async function saveSchedule() {
    // Convert grid back to intervals
    const newSchedule = DAYS.map(day => {
      const intervals = [];
      let start = null;

      HOURS.forEach(hour => {
        if (schedule[day][hour] && start === null) {
          start = hour;
        } else if (!schedule[day][hour] && start !== null) {
          intervals.push({
            from: `${start.toString().padStart(2, '0')}:00`,
            to: `${hour.toString().padStart(2, '0')}:00`
          });
          start = null;
        }
      });

      if (start !== null) {
        intervals.push({
          from: `${start.toString().padStart(2, '0')}:00`,
          to: '24:00'
        });
      }

      return { dayOfWeek: day, intervals };
    });

    await fetch(`/api/availability/${availabilityId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ schedule: newSchedule })
    });
  }

  return (
    <div
      className="availability-grid"
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
    >
      <div className="header-row">
        <div className="time-column"></div>
        {DAYS.map(day => (
          <div key={day} className="day-header">
            {day.slice(0, 3)}
          </div>
        ))}
      </div>

      {HOURS.filter(h => h >= 6 && h <= 22).map(hour => (
        <div key={hour} className="hour-row">
          <div className="time-column">
            {hour.toString().padStart(2, '0')}:00
          </div>
          {DAYS.map(day => (
            <div
              key={`${day}-${hour}`}
              className={`cell ${schedule[day]?.[hour] ? 'available' : ''}`}
              onMouseDown={() => handleMouseDown(day, hour)}
              onMouseEnter={() => handleMouseEnter(day, hour)}
            />
          ))}
        </div>
      ))}
    </div>
  );
}

Styles

.availability-grid {
  display: flex;
  flex-direction: column;
  user-select: none;
}

.header-row, .hour-row {
  display: flex;
}

.time-column {
  width: 60px;
  font-size: 12px;
  color: #666;
}

.day-header {
  flex: 1;
  text-align: center;
  font-weight: 600;
  padding: 8px;
}

.cell {
  flex: 1;
  height: 24px;
  border: 1px solid #e5e7eb;
  cursor: pointer;
  transition: background-color 0.1s;
}

.cell:hover {
  background-color: #dbeafe;
}

.cell.available {
  background-color: #22c55e;
}

Best Practices

Show timezone - Always display which timezone the schedule is in
Auto-save - Save changes automatically with debouncing
Provide presets - Quick buttons for common schedules
Copy functionality - Let users copy one day to others
Mobile support - Touch-friendly for mobile users

Next Steps