A complete, production-ready restaurant reservation system with advanced backoffice management, table mapping, OTP verification, and multi-channel communication (WhatsApp + Email + SMS).
- π Home Page with hero section, menu carousel, and gallery
- π Interactive Menu with categories, filters, and dietary information
- π Advanced Reservation System with multi-step booking flow
- πΊοΈ Visual Table Selection with interactive floor map
- π OTP Verification via Email, Phone (WhatsApp), or Both
- π€ Customer Portal to view and manage reservations
- π± Fully Mobile Responsive design
- Date, Time & Party Size Selection
- Visual calendar with availability indicators
- Dynamic time slot selection
- Service duration options
- Table Selection
- Interactive restaurant floor map
- Real-time availability
- Smart table suggestions
- Contact Information & OTP Verification
- Multi-channel verification (Email/Phone/Both)
- Automatic customer profile linking
- Additional Details
- Special occasions and requests
- Dietary restrictions and allergies
- Pre-order menu items
- Confirmation
- QR code generation
- Calendar export (.ics)
- PDF confirmation
- π Secure Authentication with Supabase Auth + Google OAuth
- π Dashboard with real-time statistics and charts
- π Reservation Management (List, Calendar, Floor Map views)
- π’ Interactive Floor Map Editor with drag-drop tables
- π₯ Customer Database & CRM
- π½οΈ Menu Management with image upload
- πΌοΈ Gallery Management with bulk upload
- π¬ Communication Center with templates and automation
- π Analytics & Reports
- βοΈ Settings (Restaurant, Reservation, Communication)
- π± WhatsApp Business API integration (primary)
- π§ Email via Resend (secondary)
- π¬ SMS via Twilio (fallback)
- π Automatic fallback chain
- π Message templates with placeholders
- π Delivery tracking and analytics
- Framework: Next.js 14 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- UI Components: shadcn/ui (Radix UI)
- State Management: Zustand + TanStack Query
- Forms: React Hook Form + Zod
- Charts: Recharts
- Floor Map: React Konva
- Icons: Lucide React
- Database: Supabase (PostgreSQL)
- Authentication: Supabase Auth
- Storage: Supabase Storage
- Edge Functions: Supabase Functions
- Email: Resend
- WhatsApp: WhatsApp Business API
- SMS: Twilio
- Payments: Stripe (optional deposits)
- QR Codes: qrcode.react
- PDFs: @react-pdf/renderer
- Node.js 18+ and npm/yarn/pnpm
- Supabase account (supabase.com)
- Resend account (resend.com)
- WhatsApp Business API access (optional but recommended)
- Twilio account (optional - for SMS fallback)
- Stripe account (optional - for deposits)
git clone <your-repo-url>
cd restaurantSABORES DE PORTUGALnpm install
# or
yarn install
# or
pnpm install- Go to supabase.com and create a new project
- Wait for the project to be ready
- Get your project credentials:
- Project URL:
https://your-project.supabase.co - Anon Key: From Settings β API
- Service Role Key: From Settings β API (keep this secret!)
- Project URL:
- Go to the SQL Editor in your Supabase dashboard
- Copy the contents of
supabase/schema.sql - Run the SQL script to create all tables, indexes, and policies
Go to Storage in your Supabase dashboard and create these buckets:
-- Create storage buckets (run in SQL Editor)
INSERT INTO storage.buckets (id, name, public) VALUES
('menu-images', 'menu-images', true),
('gallery-images', 'gallery-images', true),
('customer-photos', 'customer-photos', false),
('floor-plans', 'floor-plans', false);
-- Storage policies for menu-images
CREATE POLICY "Public can view menu images" ON storage.objects FOR SELECT
USING (bucket_id = 'menu-images');
CREATE POLICY "Admins can upload menu images" ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'menu-images' AND
EXISTS (SELECT 1 FROM admin_users WHERE user_id = auth.uid())
);
CREATE POLICY "Admins can delete menu images" ON storage.objects FOR DELETE
USING (
bucket_id = 'menu-images' AND
EXISTS (SELECT 1 FROM admin_users WHERE user_id = auth.uid())
);
-- Repeat similar policies for other buckets- Sign up through Supabase Auth (or use Google OAuth)
- Note your user ID from the Supabase Auth dashboard
- Run this SQL to make yourself an admin:
INSERT INTO admin_users (user_id, role, is_active)
VALUES ('YOUR_USER_ID_HERE', 'super_admin', true);Copy .env.example to .env.local:
cp .env.example .env.localEdit .env.local with your credentials:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
# Site
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SITE_NAME=Restaurant SABORES DE PORTUGAL
# Resend (Email)
RESEND_API_KEY=your_resend_api_key
RESEND_FROM_EMAIL=noreply@yourdomain.com
RESEND_FROM_NAME=Restaurant SABORES DE PORTUGAL
# WhatsApp Business API (optional)
WHATSAPP_API_KEY=your_api_key
WHATSAPP_PHONE_NUMBER=+1234567890
WHATSAPP_API_URL=https://api.provider.com/v1
# Twilio (SMS Fallback - optional)
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=+1234567890
# Stripe (optional)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...- Sign up at resend.com
- Add and verify your domain
- Create an API key
- Add the API key to
.env.local
Choose one of these providers:
- Meta Cloud API: Direct integration with Meta
- Twilio: WhatsApp API through Twilio
- MessageBird: WhatsApp Business Solution
Steps:
- Apply for WhatsApp Business API access
- Get approved and receive credentials
- Create message templates and get them approved
- Add credentials to
.env.local
npm run devOpen http://localhost:3000 in your browser.
restaurantSABORES DE PORTUGAL/
βββ app/
β βββ (public)/ # Public-facing pages
β β βββ page.tsx # Home page
β β βββ menu/ # Menu page
β β βββ reservation/ # Multi-step reservation flow
β β βββ my-reservations/ # Customer portal
β β βββ about/ # About page
β βββ (admin)/ # Admin panel
β β βββ admin/
β β βββ dashboard/
β β βββ reservations/
β β βββ customers/
β β βββ menu/
β β βββ gallery/
β β βββ communication/
β β βββ floor-plan/
β β βββ analytics/
β β βββ settings/
β βββ login/ # Admin login
β βββ api/ # API routes
β βββ globals.css
β βββ layout.tsx
βββ components/
β βββ ui/ # shadcn/ui components
β βββ public/ # Public website components
β βββ admin/ # Admin panel components
βββ lib/
β βββ supabase/ # Supabase clients
β βββ api/ # API functions
β βββ utils/ # Utility functions
β βββ validations/ # Zod schemas
β βββ hooks/ # Custom React hooks
βββ types/
β βββ database.ts # Database types
β βββ index.ts # Common types
βββ supabase/
β βββ schema.sql # Complete database schema
β βββ functions/ # Edge Functions
βββ public/ # Static files
βββ package.json
βββ tsconfig.json
βββ tailwind.config.ts
βββ next.config.js
The system uses PostgreSQL via Supabase with the following main tables:
- customers: Customer profiles with contact info and preferences
- customer_contacts: Links multiple contact methods to profiles
- otp_verifications: OTP codes for verification
- reservations: Main reservations table
- reservation_menu_items: Pre-ordered items
- restaurant_tables: Table configuration and floor map positions
- table_blocks: Table unavailability periods
- menu_items: Restaurant menu
- gallery_images: Image gallery
- communication_log: All sent messages tracking
- message_templates: Reusable message templates
- restaurant_settings: System configuration
- floor_plans: Multiple floor layout configurations
- admin_users: Admin roles and permissions
- audit_log: Admin action tracking
- waitlist: Customer waitlist
See supabase/schema.sql for the complete schema.
- OTP-based: No passwords required
- Multi-channel: Email, Phone (WhatsApp), or Both
- Profile Linking: Automatically links email and phone to one profile
- Verification Tracking: Tracks which methods are verified
- Supabase Auth: Email/password or Google OAuth
- Role-Based Access Control: Super Admin, Manager, Staff, View-Only
- Row Level Security (RLS): Database-level security
- Audit Logging: All admin actions tracked
- Customer enters email and/or phone number
- System generates 6-digit OTP code
- Code sent via:
- Primary: WhatsApp (if phone provided)
- Secondary: Email (if email provided or WhatsApp fails)
- Fallback: SMS (if both fail)
- Customer enters code (5-minute expiry, max 3 attempts)
- On success:
- Customer profile created or linked
- Contact method marked as verified
- Reservation process continues
- Drag-and-drop table placement
- Resize and rotate tables
- Multi-floor support
- Background image upload (floor plan)
- Table properties: Number, capacity, type, features
- Table combinations: Define which tables can combine for large parties
- Save layouts: Multiple layouts (lunch, dinner, events)
- Interactive map: Click to select tables
- Real-time availability: Based on date/time
- Smart suggestions: System recommends best tables
- Filtering: By location (window, outdoor, etc.)
- Visual status: Color-coded availability
async function sendMessage(reservation, type) {
// Try WhatsApp first (if phone verified)
if (customer.phone_verified) {
try {
await sendWhatsApp(phone, message);
return 'sent';
} catch (error) {
// Fall through to email
}
}
// Try Email second (if email verified or WhatsApp failed)
if (customer.email_verified) {
try {
await sendEmail(email, message);
return 'sent';
} catch (error) {
// Fall through to SMS
}
}
// Try SMS as last resort
try {
await sendSMS(phone, message);
return 'sent';
} catch (error) {
// All channels failed - notify admin
await notifyAdmin(error);
return 'failed';
}
}Templates support placeholders:
{name},{first_name},{last_name}{date},{time},{guests}{table},{reservation_number}{restaurant_name},{address}{otp_code},{expiry_minutes}
Example:
Hi {name}! Your table is reserved at {restaurant_name} on {date} at {time}
for {guests} guests. Table: {table}. Confirmation: {reservation_number}.
See you soon!
- Confirmation: Sent immediately after booking
- 24-hour reminder: Sent day before reservation
- 2-hour reminder: Sent 2 hours before
- Thank you: Sent after visit
- Marketing: Custom campaigns (with consent)
Create these functions in your Supabase project:
// supabase/functions/send-otp/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
serve(async (req) => {
const { contact_type, contact_value } = await req.json()
// Generate OTP
const otp_code = generateOTP(6)
// Store in database
const { error } = await supabase
.from('otp_verifications')
.insert({
contact_type,
contact_value,
otp_code,
expires_at: new Date(Date.now() + 5 * 60 * 1000)
})
// Send via WhatsApp or Email
if (contact_type === 'phone') {
await sendWhatsApp(contact_value, `Your code is: ${otp_code}`)
} else {
await sendEmail(contact_value, `Your code is: ${otp_code}`)
}
return new Response(JSON.stringify({ success: true }))
})// Validates OTP code and links customer profile// Complex availability checking with table combinations// Creates reservation, assigns tables, sends confirmation- Today's reservations count
- Pending reservations needing confirmation
- Current occupancy (live)
- Expected revenue (from pre-orders + deposits)
- No-show rate (today vs average)
- Average party size
- Reservations trend (line chart)
- Popular time slots (bar chart)
- Table utilization (pie chart)
- Peak days heatmap
- Customer retention
- Revenue by category
Edit tailwind.config.ts:
colors: {
restaurant: {
burgundy: '#8B0000', // Primary brand color
gold: '#D4AF37', // Secondary/accent
cream: '#F5F5DC', // Background
charcoal: '#333333', // Text
olive: '#556B2F', // Success states
orange: '#CC5500', // Warnings
},
}Configure via admin panel Settings page or database:
UPDATE restaurant_settings
SET setting_value = '"Your Restaurant Name"'::jsonb
WHERE setting_key = 'restaurant_name';- Push your code to GitHub
- Import project to Vercel
- Add environment variables
- Deploy
# Configure environment variables in Vercel dashboard
# Production URLs should use your domain, not localhost- Add domain in Vercel
- Configure DNS records
- Update
NEXT_PUBLIC_SITE_URLin environment variables - Verify domain in Resend for emails
- Stripe webhooks:
https://yourdomain.com/api/webhooks/stripe - WhatsApp webhooks:
https://yourdomain.com/api/webhooks/whatsapp
Run the seed script to populate test data:
npm run seedThis creates:
- Sample menu items
- Test tables
- Sample reservations
- Message templates
Create test customers:
INSERT INTO customers (first_name, last_name, email, phone, email_verified, phone_verified)
VALUES ('Test', 'Customer', 'test@example.com', '+1234567890', true, true);- Database changes: Update
supabase/schema.sql - Types: Regenerate types or update
types/database.ts - API: Create functions in
lib/api/ - Components: Add to
components/ - Pages: Create in
app/ - Test: Test locally
- Deploy: Push to git and deploy
npx supabase gen types typescript --project-id your-project-id > types/database.ts- Check Resend/WhatsApp API credentials
- Verify email domain is verified in Resend
- Check Supabase Edge Function logs
- Ensure communication_log table is tracking failures
- Verify tables exist in
restaurant_tables - Check
is_active = true - Ensure
position_xandposition_yare set - Check floor map layout_data
- Check RLS policies allow INSERT
- Verify customer_id exists
- Check foreign key constraints
- Review Supabase logs
- Verify user exists in
admin_userstable - Check
is_active = true - Ensure role is set correctly
- Check Supabase Auth session
This is a production template. Fork and customize for your needs.
MIT License - feel free to use for your restaurant projects.
For issues and questions:
- Check the troubleshooting section
- Review Supabase logs
- Check browser console for errors
- Verify environment variables
Built with β€οΈ for Restaurant SABORES DE PORTUGAL
Enterprise-grade reservation system powered by Next.js, Supabase, and modern web technologies.