Southside Flow
Southside Flow
A comprehensive restaurant management dashboard with real-time order tracking and dynamic menu customization.
A robust Next.js application providing a comprehensive administration dashboard for restaurant operations. It features real-time order management, dynamic menu configuration, staff access control, and sales analytics, all powered by Supabase.
The Chaos of Manual Restaurant Management.
Traditional restaurant operations often face significant inefficiencies due to manual order tracking, static menus requiring constant updates, and a lack of real-time sales data. This often leads to slower service, inventory discrepancies, and difficulty in making data-driven business decisions. Furthermore, managing staff access and roles can be cumbersome, impacting both operational security and workflow efficiency.
Real-time Clarity.
Effortless Operations.
Southside Flow addresses these challenges with a modern, full-stack Next.js application, leveraging Supabase for a robust backend. It provides a real-time admin dashboard for efficient order processing, dynamic menu management with image uploads and category reordering, and detailed sales analytics powered by Chart.js. Secure, role-based access control is implemented via custom hooks and Supabase Auth, ensuring that staff can perform their duties effectively while maintaining data integrity. API routes handle seamless data interactions, from fetching live orders and menu items to exporting comprehensive sales reports.
Real-time Data Sync via Supabase.
System Core // Efficient Large Data Export with Generator
async function* getOrdersGenerator(supabase) {
let rangeStart = 0;
const BATCH_SIZE = 100;
while (true) {
const { data: orders, error } = await supabase
.from('orders')
.select(`
id, order_number, status, total_price, created_at, order_type,
orderitems (id, quantity, price_at_order, variant_name,
menuitems (name, category),
orderitem_addons (addon_id, price_at_order, addons (name)))
`)
.order('created_at', { ascending: false })
.range(rangeStart, rangeStart + BATCH_SIZE - 1);
if (error) throw error;
if (!orders || orders.length === 0) break;
yield orders;
if (orders.length < BATCH_SIZE) break;
rangeStart += BATCH_SIZE;
}
}
Next Case Study