# Mawidi Demo Scheduler

A complete, production-ready appointment booking system for Next.js 14 with bilingual support (English/Arabic), n8n webhook integration, and Calendly-like user experience.

## Features

### Core Functionality

- ✅ **4-Step Booking Flow**: Duration → Date & Time → User Info → Confirmation
- ✅ **Working Hours Enforcement**: Sun-Thu 2-7PM, Fri-Sat 7AM-5PM (UTC+3)
- ✅ **Advance Booking**: 12-hour minimum advance notice
- ✅ **Booking Window**: 21-day scheduling window
- ✅ **Time Slot Generation**: Smart 30-minute intervals respecting business hours
- ✅ **Calendar View**: Interactive month calendar with availability indicators
- ✅ **Form Validation**: Real-time validation with error messages
- ✅ **Input Sanitization**: XSS prevention on all user inputs

### Internationalization

- ✅ **Bilingual Support**: Full English and Arabic translations
- ✅ **RTL Layout**: Right-to-left layout for Arabic
- ✅ **Localized Dates**: Date formatting per language conventions
- ✅ **Timezone Handling**: UTC+3 (Riyadh) with user timezone detection

### Integration & Persistence

- ✅ **n8n Webhook**: POST booking data to your n8n workflow
- ✅ **Local Storage**: Draft booking persistence across page refreshes
- ✅ **ICS Calendar Export**: Download calendar file for user's calendar app
- ✅ **Email Notifications**: Via n8n workflow integration
- ✅ **Google Calendar**: Automatic event creation via n8n

### User Experience

- ✅ **Dark Mode**: Full dark theme support
- ✅ **Responsive Design**: Mobile-first, works on all screen sizes
- ✅ **Accessibility**: ARIA labels, keyboard navigation, screen reader support
- ✅ **Loading States**: Smooth loading indicators
- ✅ **Success Animations**: Celebration UI on booking confirmation
- ✅ **Error Handling**: Clear error messages and recovery options

## Installation

### Prerequisites

- Next.js 14 (App Router)
- React 18
- TypeScript 5
- Tailwind CSS 3

### Steps

1. **Copy the component directory**:

```bash
cp -r src/components/DemoScheduler /your-project/src/components/
```

2. **Install dependencies** (if not already installed):

```bash
npm install date-fns
```

3. **Configure environment variables**:

```bash
# Create .env.local
cp src/components/DemoScheduler/.env.example .env.local

# Edit .env.local and add your n8n webhook URL
NEXT_PUBLIC_N8N_WEBHOOK_URL=https://your-n8n-instance.com/webhook/demo-booking
```

4. **Configure Tailwind CSS** (add to `tailwind.config.ts`):

```typescript
module.exports = {
  content: [
    // ... existing paths
    "./src/components/DemoScheduler/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        "brand-green": "#10b981",
        "brand-ink": "#1e293b",
        "brand-sand": "#fef3c7",
      },
    },
  },
};
```

## Usage

### Basic Example

```tsx
import { DemoScheduler } from "@/components/DemoScheduler";

export default function DemoPage() {
  return (
    <div className="container mx-auto py-12">
      <DemoScheduler
        language="en"
        webhookUrl={process.env.NEXT_PUBLIC_N8N_WEBHOOK_URL}
        onBookingComplete={(data, reference) => {
          console.log("Booking completed:", reference);
          // Optional: Track analytics, show custom message, etc.
        }}
      />
    </div>
  );
}
```

### With Custom Styling

```tsx
<DemoScheduler
  language="ar"
  className="max-w-5xl"
  webhookUrl={process.env.NEXT_PUBLIC_N8N_WEBHOOK_URL}
  onBookingComplete={(data, reference) => {
    // Custom success handling
    gtag("event", "booking_complete", {
      booking_reference: reference,
      duration: data.duration,
    });
  }}
  onCancel={() => {
    // Handle user cancellation
    router.push("/");
  }}
/>
```

### Advanced Usage with Custom Hooks

You can use the individual hooks for custom implementations:

```tsx
import {
  useAvailability,
  useBookingForm,
  useTimezone,
} from "@/components/DemoScheduler";

export default function CustomBooking() {
  const { availableDates, getTimeSlotsForDate } = useAvailability({
    duration: 30,
  });

  const { bookingData, updateBookingData, submitBooking } = useBookingForm({
    webhookUrl: process.env.NEXT_PUBLIC_N8N_WEBHOOK_URL,
  });

  const { timezone, hasTimezoneMismatch } = useTimezone({
    autoDetect: true,
  });

  // Custom implementation
  return <div>{/* Your custom UI */}</div>;
}
```

## Configuration

### Scheduler Configuration

Edit `src/components/DemoScheduler/config/schedulerConfig.ts`:

```typescript
export const SCHEDULER_CONFIG = {
  // Timezone for all bookings (IANA timezone identifier)
  timezone: "Asia/Riyadh", // UTC+3

  // Available durations in minutes
  availableDurations: [30, 60],

  // Working hours per day (24-hour format)
  workingHours: {
    sunday: { start: 14, end: 19 }, // 2 PM - 7 PM
    monday: { start: 14, end: 19 },
    tuesday: { start: 14, end: 19 },
    wednesday: { start: 14, end: 19 },
    thursday: { start: 14, end: 19 },
    friday: { start: 7, end: 17 }, // 7 AM - 5 PM
    saturday: { start: 7, end: 17 },
  },

  // Minimum hours in advance for booking
  minimumAdvanceHours: 12,

  // Maximum days in future for booking
  maxBookingDays: 21,

  // Time slot interval in minutes
  slotInterval: 30,

  // Buffer time between slots in minutes
  bufferTime: 0,
};
```

### Customizing Labels

All UI text is in bilingual label objects. Edit components to customize:

```typescript
const labels = {
  en: {
    title: "Schedule Your Demo",
    subtitle: "Choose a convenient time",
    // ... more labels
  },
  ar: {
    title: "احجز جلسة تجريبية",
    subtitle: "اختر الوقت المناسب",
    // ... more labels
  },
};
```

## API Reference

### DemoScheduler Props

| Prop                | Type                  | Default     | Description                     |
| ------------------- | --------------------- | ----------- | ------------------------------- |
| `language`          | `'en' \| 'ar'`        | `'en'`      | Display language                |
| `className`         | `string`              | `''`        | Additional CSS classes          |
| `webhookUrl`        | `string`              | `undefined` | n8n webhook URL for submissions |
| `onBookingComplete` | `(data, ref) => void` | `undefined` | Success callback                |
| `onCancel`          | `() => void`          | `undefined` | Cancel/back callback            |

### BookingData Type

```typescript
interface BookingData {
  duration: number; // 30 or 60 minutes
  selectedDate: Date; // Booking date
  selectedTime: string; // Time in HH:MM format
  timezone: string; // IANA timezone
  userInfo: {
    name: string; // User's full name
    email: string; // Valid email address
    phone?: string; // Optional phone number
    message: string; // User's message/requirements
  };
}
```

### Webhook Payload

When a booking is submitted, the following JSON is sent to your webhook:

```json
{
  "bookingReference": "BK-2024-ABC123",
  "duration": 30,
  "selectedDate": "2024-01-15T00:00:00.000Z",
  "selectedTime": "14:00",
  "timezone": "Asia/Riyadh",
  "userInfo": {
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+966501234567",
    "message": "Looking forward to the demo"
  },
  "submittedAt": "2024-01-10T10:30:00.000Z"
}
```

## n8n Webhook Integration

See [N8N_WEBHOOK_SETUP.md](./N8N_WEBHOOK_SETUP.md) for complete n8n workflow setup guide.

**Quick Setup:**

1. Create webhook in n8n (POST endpoint)
2. Add your webhook URL to `.env.local`
3. Configure email/calendar nodes in n8n
4. Test with provided curl command

## Utility Functions

The scheduler exports several utility functions you can use:

### Date Helpers

```typescript
import {
  formatDateForDisplay,
  formatTimeForDisplay,
  isWithinBookingWindow,
  isBusinessDay,
  isWithinBusinessHours,
  meetsMinimumAdvance,
} from "@/components/DemoScheduler";

// Format date for display
const formatted = formatDateForDisplay(new Date(), "en");
// Returns: "Monday, January 15, 2024"

// Check if date is within 21-day booking window
const canBook = isWithinBookingWindow(new Date());

// Check if day has working hours
const isOpen = isBusinessDay(new Date());
```

### Validation Helpers

```typescript
import {
  validateEmail,
  validatePhoneNumber,
  validateName,
  sanitizeInput,
} from "@/components/DemoScheduler";

// Validate email
const isValid = validateEmail("user@example.com");

// Sanitize user input (XSS prevention)
const clean = sanitizeInput('<script>alert("xss")</script>');
```

### Storage Helpers

```typescript
import {
  saveBookingToStorage,
  getBookingFromStorage,
  clearBookingFromStorage,
} from "@/components/DemoScheduler";

// Save draft booking
saveBookingToStorage(bookingData);

// Retrieve draft booking
const draft = getBookingFromStorage();

// Clear stored data
clearBookingFromStorage();
```

## Styling

### CSS Module

The component includes `DemoScheduler.module.css` with:

- Success checkmark animations
- Calendar hover effects
- Form focus states
- RTL support utilities
- Dark mode transitions
- Accessibility features (reduced motion)
- Custom scrollbar styling

### Customizing Colors

Override Tailwind colors in your `tailwind.config.ts`:

```typescript
theme: {
  extend: {
    colors: {
      'brand-green': '#your-color',
      'brand-ink': '#your-color',
      'brand-sand': '#your-color',
    },
  },
}
```

### Dark Mode

The scheduler automatically supports dark mode via Tailwind's `dark:` variant. Enable dark mode in your app:

```typescript
// tailwind.config.ts
module.exports = {
  darkMode: "class", // or 'media'
  // ...
};
```

## Accessibility

The scheduler is built with accessibility in mind:

- ✅ **Semantic HTML**: Proper heading hierarchy, form labels
- ✅ **ARIA Attributes**: Labels, descriptions, live regions
- ✅ **Keyboard Navigation**: Tab order, focus management
- ✅ **Screen Readers**: Descriptive labels and announcements
- ✅ **Reduced Motion**: Respects `prefers-reduced-motion`
- ✅ **Color Contrast**: WCAG AA compliant
- ✅ **Focus Indicators**: Visible focus states

## Browser Support

- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile browsers (iOS Safari, Chrome Mobile)

## Troubleshooting

### Webhook Not Receiving Data

**Problem**: Bookings submit but n8n webhook doesn't receive data.

**Solutions**:

1. Check webhook URL is correct in `.env.local`
2. Verify n8n workflow is activated
3. Check n8n execution logs for errors
4. Test webhook with curl (see N8N_WEBHOOK_SETUP.md)
5. Ensure CORS is configured if webhook is on different domain

### Time Slots Not Showing

**Problem**: Calendar shows dates but no time slots appear.

**Solutions**:

1. Check selected date is a business day (not all weekends blocked)
2. Verify date is within 21-day booking window
3. Check browser console for date generation errors
4. Ensure `workingHours` config matches your needs

### Timezone Issues

**Problem**: Times displayed don't match expected timezone.

**Solutions**:

1. Verify `SCHEDULER_CONFIG.timezone` is set to `'Asia/Riyadh'`
2. Check browser's timezone detection is working
3. Test with different browsers/devices
4. Look for timezone mismatch warning in UI

### Form Validation Not Working

**Problem**: Form submits with invalid data or validation doesn't show.

**Solutions**:

1. Check that all required fields have values
2. Verify email format is correct
3. Ensure phone number includes country code
4. Look for validation error messages in UI
5. Check browser console for validation errors

### Dark Mode Issues

**Problem**: Dark mode styling not applying correctly.

**Solutions**:

1. Ensure Tailwind's `darkMode: 'class'` is configured
2. Check your app's dark mode toggle is working
3. Verify `dark:` variants are in component classes
4. Test in different browsers

### RTL Layout Problems

**Problem**: Arabic layout doesn't display right-to-left.

**Solutions**:

1. Check `language` prop is set to `'ar'`
2. Verify `dir="rtl"` attribute is applied
3. Ensure Tailwind's `rtl:` variants are available
4. Test with actual Arabic text

## Performance

### Optimization Tips

1. **Static Generation**: Pre-generate available dates at build time
2. **Memoization**: useAvailability uses useMemo for slot calculations
3. **Debouncing**: Form validation debounced on blur, not every keystroke
4. **Lazy Loading**: Components load only when step is active
5. **Local Storage**: Reduces re-calculations on page refresh

### Bundle Size

Estimated bundle impact:

- Component code: ~45KB gzipped
- Dependencies (date-fns): ~15KB gzipped
- Total: ~60KB gzipped

## Testing

### Manual Testing Checklist

- [ ] Select 30-minute duration
- [ ] Select 60-minute duration
- [ ] Navigate calendar months
- [ ] Select available date
- [ ] Select time slot
- [ ] Fill user information form
- [ ] Test email validation
- [ ] Test phone validation
- [ ] Review confirmation details
- [ ] Submit booking
- [ ] Download ICS calendar file
- [ ] Book another session
- [ ] Test Arabic language
- [ ] Test RTL layout
- [ ] Test dark mode
- [ ] Test on mobile device
- [ ] Verify webhook receives data
- [ ] Check email notification sent

### Automated Testing

Example test with React Testing Library:

```typescript
import { render, screen, fireEvent } from '@testing-library/react';
import { DemoScheduler } from '@/components/DemoScheduler';

test('completes booking flow', async () => {
  render(<DemoScheduler language="en" />);

  // Step 1: Duration
  fireEvent.click(screen.getByText('30 minutes'));
  fireEvent.click(screen.getByText('Next'));

  // Step 2: Date & Time
  const availableDate = screen.getByText('15');
  fireEvent.click(availableDate);
  const timeSlot = screen.getByText('2:00 PM');
  fireEvent.click(timeSlot);
  fireEvent.click(screen.getByText('Next'));

  // Step 3: User Info
  fireEvent.change(screen.getByLabelText('Full Name'), {
    target: { value: 'John Doe' },
  });
  fireEvent.change(screen.getByLabelText('Email Address'), {
    target: { value: 'john@example.com' },
  });
  fireEvent.click(screen.getByText('Next'));

  // Step 4: Confirm
  fireEvent.click(screen.getByText('Confirm Booking'));

  // Success
  expect(await screen.findByText(/booking confirmed/i)).toBeInTheDocument();
});
```

## Migration Guide

### From Calendly

If migrating from Calendly:

1. **Events → Durations**: Map Calendly event types to `availableDurations`
2. **Hours → workingHours**: Convert Calendly availability to config format
3. **Webhooks**: Replace Calendly webhook with n8n integration
4. **Branding**: Customize colors and labels
5. **Email**: Setup n8n email node (similar to Calendly notifications)

### From Other Schedulers

1. Export existing booking data
2. Map data format to BookingData interface
3. Configure working hours to match current setup
4. Test thoroughly before switching

## FAQ

**Q: Can I use this with other backend systems besides n8n?**
A: Yes! The `webhookUrl` accepts any POST endpoint. You can integrate with Zapier, Make.com, or your own API.

**Q: How do I add more duration options?**
A: Edit `SCHEDULER_CONFIG.availableDurations` array in `schedulerConfig.ts`.

**Q: Can I customize the working hours per location?**
A: Currently, working hours are global. For multi-location support, you'd need to extend the config with location-based hours.

**Q: Does it support recurring bookings?**
A: No, currently only single-session bookings. Recurring bookings would require additional implementation.

**Q: Can I add custom fields to the booking form?**
A: Yes! Extend the `UserInfo` type and `BookingForm` component with your custom fields.

**Q: How do I prevent double-bookings?**
A: Pass existing bookings to `useAvailability` hook's `existingBookings` prop. These slots will be marked as unavailable.

**Q: Can I integrate with Google Calendar directly?**
A: The component generates ICS files for user download. For automatic Google Calendar integration, use the n8n Google Calendar node (see N8N_WEBHOOK_SETUP.md).

## Contributing

This is a self-contained component. To contribute:

1. Test changes thoroughly
2. Update documentation
3. Ensure TypeScript types are correct
4. Follow existing code patterns
5. Test both languages (English/Arabic)
6. Test both themes (light/dark)

## License

This component is part of the Mawidi project. All rights reserved.

## Support

For issues or questions:

- Check this README and N8N_WEBHOOK_SETUP.md
- Review browser console for errors
- Test with provided curl commands
- Contact Mawidi support team

## Changelog

### Version 1.0.0 (2024)

- Initial release
- 4-step booking flow
- Bilingual support (English/Arabic)
- n8n webhook integration
- Working hours enforcement
- Local storage persistence
- ICS calendar export
- Dark mode support
- Full accessibility support
