Frontend Overview
The JustCall frontend is a React application built with modern tooling and best practices.
Tech Stack
| Technology | Purpose |
|---|---|
| React 18 | UI framework |
| TypeScript | Type safety |
| Vite | Build tool |
| Tailwind CSS | Styling |
| shadcn/ui | Component library |
| TanStack Query | Data fetching |
| React Router | Navigation |
| Supabase Client | Backend integration |
Project Structure
src/
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ └── restaurant-settings/ # Feature components
├── contexts/ # React Context providers
├── hooks/ # Custom React hooks
├── pages/ # Route components
├── integrations/ # External service integrations
│ └── supabase/ # Supabase client & types
├── lib/ # Utility functions
└── types/ # TypeScript type definitions
Key Features
1. Restaurant Dashboard
- Order management (live updates)
- Call history
- Analytics overview
2. Menu Management
- Category/item CRUD
- AI-powered menu import (OCR)
- Ingredient management
3. Settings
Multiple configuration tabs:
- General (name, address, contact)
- Opening hours
- Voice settings
- WhatsApp settings
- Delivery zones
- AI behavior
- Billing
- Notifications
- Technical/Debug
4. Kitchen View
Real-time order display for kitchen staff.
Entry Point
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import App from './App';
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<App />
</BrowserRouter>
</QueryClientProvider>
</React.StrictMode>
);
Routing
// src/App.tsx
import { Routes, Route } from 'react-router-dom';
function App() {
return (
<RestaurantProvider>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/orders" element={<Orders />} />
<Route path="/menu" element={<Menu />} />
<Route path="/settings" element={<RestaurantSettings />} />
<Route path="/kitchen" element={<KitchenView />} />
<Route path="/calls" element={<CallHistory />} />
</Routes>
</RestaurantProvider>
);
}
Authentication
Supabase Auth handles user sessions:
// src/hooks/useAuth.ts
import { useEffect, useState } from 'react';
import { supabase } from '@/integrations/supabase/client';
export function useAuth() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null);
setLoading(false);
});
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, session) => setUser(session?.user ?? null)
);
return () => subscription.unsubscribe();
}, []);
return { user, loading };
}
Data Fetching
TanStack Query for all API calls:
// Example: Fetching orders
const { data: orders, isLoading } = useQuery({
queryKey: ['orders', restaurantId],
queryFn: async () => {
const { data, error } = await supabase
.from('orders')
.select('*')
.eq('restaurant_id', restaurantId)
.order('created_at', { ascending: false });
if (error) throw error;
return data;
},
});
Real-time Updates
Supabase Realtime for live data:
// src/hooks/useRealtimeOrders.ts
useEffect(() => {
const channel = supabase
.channel('orders')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'orders',
filter: `restaurant_id=eq.${restaurantId}`,
},
(payload) => {
queryClient.invalidateQueries(['orders', restaurantId]);
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [restaurantId]);
Styling
Tailwind CSS with custom configuration:
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#25c2a0',
dark: '#21af90',
},
},
},
},
plugins: [require('tailwindcss-animate')],
};
Component Library
Using shadcn/ui components:
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';