New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

booking-ui

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

booking-ui

React booking widget component for ZvenBook booking system

latest
Source
npmnpm
Version
0.0.1
Version published
Maintainers
1
Created
Source

@booking/ui - Booking Widget Component

A production-ready, fully customizable React booking widget component for embedding appointment scheduling directly into your website.

✨ Features

  • 🎯 Complete Booking Flow - Service selection → Date/Time → Contact details → Confirmation
  • 🌍 Internationalization - Built-in English & Swedish, easy to add more languages
  • 🎨 Fully Customizable - Match your brand with CSS variables and className overrides
  • 📱 Mobile Responsive - Works beautifully on all devices
  • 🌙 Dark Mode Support - Automatic dark mode detection
  • Form Validation - Real-time email validation and error handling
  • Loading States - Smooth loading indicators throughout
  • Accessible - Built with accessibility in mind

📦 Installation

npm install @booking/ui
# or
yarn add @booking/ui
# or
pnpm add @booking/ui

Peer Dependencies

This package requires React 18+ and React DOM 18+:

npm install react react-dom

🚀 Quick Start

import { BookingStepsWidget } from '@booking/ui';

function App() {
  return (
    <BookingStepsWidget
      baseUrl="https://your-api-url.com"
      tenantId="your-tenant-id"
    />
  );
}

📖 Basic Usage

Minimal Example

import { BookingStepsWidget } from '@booking/ui';

<BookingStepsWidget
  baseUrl="https://api.example.com"
  tenantId="tenant-123"
/>

With Customization

import { BookingStepsWidget, swedishLabels } from '@booking/ui';

<BookingStepsWidget
  baseUrl="https://api.example.com"
  tenantId="tenant-123"
  weekStartsOn="monday"
  labels={swedishLabels}
  layout="wizard"
  spacing="cozy"
  radius="lg"
  styles={{
    primaryColor: '#0066FF',
    accentColor: '#00AA44',
    serviceCardClassName: 'hover:shadow-lg',
    submitButtonClassName: 'w-full font-bold',
  }}
/>

🔧 Props Reference

Required Props

PropTypeDescription
baseUrlstringBase URL of your booking API (e.g., "https://api.example.com")
tenantIdstringYour tenant ID from the booking system

Optional Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes for the root container
layout"horizontal" | "vertical" | "wizard""horizontal"Layout style for the widget
spacing"compact" | "normal" | "cozy""normal"Spacing between elements
radius"none" | "sm" | "md" | "lg" | "full""md"Border radius for elements
color"accent" | "primary" | "muted" | "transparent""accent"Color scheme variant
weekStartsOn"sunday" | "monday""sunday"Calendar week start day
labelsBookingLabelsdefaultLabelsTranslation labels (see Internationalization)
stylesBookingWidgetStyles-Styling customization (see Styling)

🎨 Styling Customization

The widget can be customized in two ways:

1. CSS Custom Properties (Theme Colors)

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  styles={{
    primaryColor: '#0066FF',    // Primary buttons, progress bars
    accentColor: '#00AA44',      // Highlights, active states
    borderColor: '#E0E0E0',      // Borders
    textColor: '#1F2937',       // Text color
    backgroundColor: '#FFFFFF',  // Background
  }}
/>

2. className Overrides (Fine-grained Control)

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  styles={{
    // Stepper component
    stepperClassName: 'custom-stepper',
    
    // Calendar component
    calendarClassName: 'shadow-lg border-2',
    
    // Service selection cards
    serviceCardClassName: 'hover:scale-105 transition-transform',
    
    // Time slot buttons
    timeSlotClassName: 'bg-blue-600 hover:bg-blue-700 text-white',
    
    // Form inputs
    inputClassName: 'font-sans text-base border-2',
    
    // Navigation buttons (Back/Next)
    buttonClassName: 'px-6 py-3 rounded-lg',
    
    // Submit button (overrides buttonClassName)
    submitButtonClassName: 'w-full font-bold uppercase tracking-wide',
  }}
/>

Complete Styling Example

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  styles={{
    // Theme colors
    primaryColor: '#0066FF',
    accentColor: '#00AA44',
    
    // Component styling
    serviceCardClassName: cn(
      'border-2 hover:border-accent transition-all',
      'shadow-sm hover:shadow-md'
    ),
    timeSlotClassName: 'bg-primary hover:bg-primary/90 text-white',
    inputClassName: 'border-2 focus:ring-2 focus:ring-primary',
    submitButtonClassName: 'w-full bg-primary hover:bg-primary/90',
  }}
/>

🌍 Internationalization

Built-in Languages

English (Default)

import { BookingStepsWidget, defaultLabels } from '@booking/ui';

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  labels={defaultLabels} // Optional, already default
/>

Swedish

import { BookingStepsWidget, swedishLabels } from '@booking/ui';

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  labels={swedishLabels}
  weekStartsOn="monday" // Swedish calendar starts Monday
/>

Custom Labels

import { BookingStepsWidget, defaultLabels, BookingLabels } from '@booking/ui';

const frenchLabels: BookingLabels = {
  ...defaultLabels,
  stepService: 'Service',
  stepDateTime: 'Date et heure',
  stepDetails: 'Vos coordonnées',
  chooseService: 'Choisir un service',
  selectDate: 'Sélectionner une date',
  loadingTimes: 'Chargement des horaires...',
  noSlotsAvailable: 'Aucun créneau disponible.',
  yourDetails: 'Vos coordonnées',
  firstNamePlaceholder: 'Prénom',
  lastNamePlaceholder: 'Nom',
  emailPlaceholder: 'Email',
  phonePlaceholder: 'Téléphone (optionnel)',
  back: 'Retour',
  next: 'Suivant',
  bookAppointment: 'Réserver',
  booking: 'Réservation…',
  bookingConfirmed: 'Réservation confirmée',
  changeService: 'Service sélectionné • modifier',
  changeDate: '{date} • modifier',
  changeTime: '{time} • modifier',
  dayLabels: {
    sunday: 'Di',
    monday: 'Lu',
    tuesday: 'Ma',
    wednesday: 'Me',
    thursday: 'Je',
    friday: 'Ve',
    saturday: 'Sa',
  },
};

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  labels={frenchLabels}
/>

Label Interface

interface BookingLabels {
  // Stepper labels
  stepService: string;
  stepDateTime: string;
  stepDetails: string;
  
  // Service selection
  chooseService: string;
  
  // Date & time selection
  selectDate: string;
  loadingTimes: string;
  noSlotsAvailable: string;
  
  // Calendar day labels
  dayLabels: {
    sunday: string;
    monday: string;
    tuesday: string;
    wednesday: string;
    thursday: string;
    friday: string;
    saturday: string;
  };
  
  // Contact form
  yourDetails: string;
  firstNamePlaceholder: string;
  lastNamePlaceholder: string;
  emailPlaceholder: string;
  phonePlaceholder: string;
  
  // Navigation
  back: string;
  next: string;
  bookAppointment: string;
  booking: string;
  
  // Success
  bookingConfirmed: string;
  
  // Change buttons
  changeService: string;
  changeDate: string; // Use {date} placeholder
  changeTime: string; // Use {time} placeholder
}

📱 Layout Options

Horizontal Layout

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  layout="horizontal"
/>

Vertical Layout

<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  layout="vertical"
/>
<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  layout="wizard"
  spacing="cozy"
/>

💡 Complete Examples

Basic Integration

'use client';

import { BookingStepsWidget } from '@booking/ui';

export default function BookingPage() {
  return (
    <div className="container mx-auto p-6">
      <h1 className="text-3xl font-bold mb-6">Book an Appointment</h1>
      <BookingStepsWidget
        baseUrl={process.env.NEXT_PUBLIC_API_URL || ''}
        tenantId={process.env.NEXT_PUBLIC_TENANT_ID || ''}
      />
    </div>
  );
}

Custom Styled Widget

import { BookingStepsWidget } from '@booking/ui';
import { cn } from '@/lib/utils';

export function CustomBookingWidget() {
  return (
    <div className="max-w-2xl mx-auto">
      <BookingStepsWidget
        baseUrl="https://api.example.com"
        tenantId="tenant-123"
        layout="wizard"
        spacing="cozy"
        radius="lg"
        styles={{
          primaryColor: '#0066FF',
          accentColor: '#00AA44',
          serviceCardClassName: cn(
            'border-2 border-gray-200',
            'hover:border-accent hover:shadow-lg',
            'transition-all duration-200'
          ),
          timeSlotClassName: 'bg-primary text-white hover:bg-primary/90',
          inputClassName: 'border-2 focus:ring-2 focus:ring-primary',
          submitButtonClassName: 'w-full bg-primary hover:bg-primary/90 font-bold',
        }}
      />
    </div>
  );
}

Multi-language Support

import { BookingStepsWidget, swedishLabels, defaultLabels } from '@booking/ui';
import { useRouter } from 'next/router';

export function LocalizedBookingWidget() {
  const router = useRouter();
  const locale = router.locale || 'en';
  
  const labels = locale === 'sv' ? swedishLabels : defaultLabels;
  const weekStartsOn = locale === 'sv' ? 'monday' : 'sunday';
  
  return (
    <BookingStepsWidget
      baseUrl={process.env.NEXT_PUBLIC_API_URL || ''}
      tenantId={process.env.NEXT_PUBLIC_TENANT_ID || ''}
      labels={labels}
      weekStartsOn={weekStartsOn}
    />
  );
}

With Tailwind CSS

If you're using Tailwind CSS, make sure to include the UI package styles:

// In your app's main CSS file
@import '@booking/ui/styles.css';

// Or import directly in your component
import '@booking/ui/styles.css';

Custom CSS Styling

/* Override default styles */
.booking-widget {
  --booking-primary: #0066FF;
  --booking-accent: #00AA44;
}

/* Custom service cards */
.custom-service-card {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

/* Custom buttons */
.custom-submit-button {
  background: #00AA44;
  padding: 1rem 2rem;
  font-weight: bold;
  text-transform: uppercase;
}
<BookingStepsWidget
  baseUrl={baseUrl}
  tenantId={tenantId}
  className="booking-widget"
  styles={{
    serviceCardClassName: 'custom-service-card',
    submitButtonClassName: 'custom-submit-button',
  }}
/>

🎯 Best Practices

1. Environment Variables

Always use environment variables for API configuration:

// .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_TENANT_ID=your-tenant-id

// Component
<BookingStepsWidget
  baseUrl={process.env.NEXT_PUBLIC_API_URL || ''}
  tenantId={process.env.NEXT_PUBLIC_TENANT_ID || ''}
/>

2. Error Handling

The component handles errors internally, but you can wrap it for additional error handling:

import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error }) {
  return (
    <div className="p-4 bg-red-50 border border-red-200 rounded">
      <p className="text-red-800">Something went wrong: {error.message}</p>
    </div>
  );
}

<ErrorBoundary FallbackComponent={ErrorFallback}>
  <BookingStepsWidget {...props} />
</ErrorBoundary>

3. Loading States

The component includes built-in loading states, but you can add a wrapper:

import { Suspense } from 'react';

<Suspense fallback={<div>Loading booking widget...</div>}>
  <BookingStepsWidget {...props} />
</Suspense>

4. Accessibility

The component is accessible by default, but ensure your page has proper structure:

<main>
  <h1>Book an Appointment</h1>
  <BookingStepsWidget {...props} />
</main>

🔍 API Requirements

The widget expects your API to implement these endpoints:

  • GET /api/services?tenantId={tenantId} - List services
  • GET /api/service-availability - Get available time slots
  • POST /api/bookings - Create booking

See the main README for complete API documentation.

🐛 Troubleshooting

Widget Not Loading

  • Check API URL: Ensure baseUrl is correct and accessible
  • Check Tenant ID: Verify tenantId exists in your system
  • Check CORS: Ensure your API allows requests from your domain
  • Check Console: Look for errors in browser console

Styling Not Applying

  • Import Styles: Make sure to import @booking/ui/styles.css
  • Tailwind Config: Ensure Tailwind is configured correctly
  • CSS Specificity: Your custom styles might need higher specificity
  • Dark Mode: Check if dark mode classes are interfering

Calendar Not Showing

  • Week Start: Verify weekStartsOn matches your locale
  • Date Format: Ensure API returns dates in ISO format
  • Timezone: Check timezone handling in API responses

📚 TypeScript Support

Full TypeScript support is included. Import types as needed:

import type {
  BookingStepsWidget,
  BookingLabels,
  BookingWidgetStyles,
} from '@booking/ui';

🤝 Contributing

Contributions welcome! Please see the main Contributing Guide.

📄 License

MIT License - see LICENSE for details.

Need Help? Check out the main documentation or open an issue.

Keywords

booking

FAQs

Package last updated on 30 Oct 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts