
The brief said "website." The problem said something else.
Paddy's Burger is a smash burger restaurant in Nyíregyháza with a growing local following. Delivery ran through Wolt and Foodora, which handled that side well. But for in-store pickup, the restaurant had no digital channel at all. No way to take orders ahead of time. No direct customer relationships. No email addresses. No order history. No way to reward regulars.
A landing page would have solved the brief. It wouldn't have solved the problem.
What I actually built
Four systems, one codebase:
- A pre-order flow that gives the restaurant a direct channel for in-store pickup orders.
- A loyalty program where returning customers earn points and redeem rewards via QR codes.
- A staff dashboard that gives the kitchen real-time visibility into incoming orders.
- A catering module (in development) that opens up a new market: events, offices, bulk orders.
The pre-order and loyalty systems serve existing customers better. Catering expands the audience entirely. Each feature emerged from a real need, not a feature wishlist.
Design decisions
Most food brands default to red, orange, yellow. Paddy's went the opposite direction: a saturated blue that makes the food photography pop through contrast. When every competitor is warm-toned, a cool palette creates instant recognition.
The typography is bold and condensed. Headlines read from a distance on mobile. Body text stays legible without competing with the images. The visual hierarchy does the work: you see the burger first, the name second, the price third.
GSAP handles scroll-triggered animations. Sections reveal on entry, menu items stagger in with 50ms delays, the hero parallaxes subtly. The motion serves a purpose: it directs attention toward the order CTA. Animations run at the same snappy 350ms base duration on all devices. ScrollTrigger thresholds are tuned separately for mobile (80% viewport) and desktop (75%).
Every screen was designed mobile-first. The majority of traffic arrives from social media links tapped on phones.
Pre-order system
The first instinct was a simple form: name, phone, items, time. But the pattern I kept seeing was the same. Customers order too much at once, pick unrealistic times, and the kitchen has no way to push back.
The flow uses progressive disclosure. You pick your burgers first. Add-ons appear only after. Then the pickup time. Then your contact info. Each step narrows the options. The customer never faces a long form. The kitchen never receives an order they can't fulfill.
Pickup time slots are generated server-side from the restaurant's actual opening hours, with a 30-minute preparation buffer and 15-minute intervals. The system blocks requests outside operating windows.
After checkout, the customer lands on a real-time tracking page. A circular progress ring visualizes the five-step journey with perceived progress mapping: the ring jumps to 5% on submission, 30% on acceptance, 65% during preparation, 100% on ready. An elapsed timer shows minutes since the order was placed. The order details expand into a collapsible section with item thumbnails and prices.
The kitchen gets an instant notification. No phone call needed.
Two details that took longer than expected. The Hungarian deposit return system (DRS) adds 50 HUF per bottled beverage, and the line item has to be visible, separate, and legally correct. Hungarian phone numbers arrive in at least six different formats; the parser normalizes all of them before validation.
Six automated emails follow the order lifecycle: confirmation with itemized receipt, staff alert with guest details, acceptance notification, preparation update, ready-for-pickup with the restaurant's address and phone, and a cancellation message if needed. All templated in React Email, all in Hungarian, all matching the site's visual language.
Loyalty program
Paper stamp cards get lost. App-based programs have download friction. The middle ground: a web-based points system tied to the user account.
Signup goes through Paddy's Club, with Google OAuth for low friction or email/password as fallback. The auth flow includes real-time password validation (8+ characters, mixed case, numbers), email verification via 6-digit OTP, and rate limiting: 5 attempts per minute, account lockout after 10 failures with a visible countdown timer.
Every order earns points, roughly 1 per 140 HUF spent. Seven reward tiers, from fries at 5 points to a Paddy's Special burger at 18. When someone redeems a reward, the system generates a unique QR code valid for 7 days. Staff scan it at the counter. No paper, no forgotten cards, no manual tracking.
The customer sees a radial progress indicator toward their next reward. On redemption: confetti in the brand colors (blue, yellow, green) fires from both sides of the screen. Small details. But they turn a transaction into a habit.
Staff dashboard
The staff dashboard solves a different problem. Customers need clarity and simplicity. The kitchen needs density and speed.
Orders flow through five states: new, accepted, preparing, ready, picked up. Each state is color-coded with real-time counts visible at a glance. The kitchen view strips away everything non-essential: large type, sticky header, live clock, oversized status pills.
Staff transition orders with a single tap. The system logs every state change with timestamps and user attribution.
Loyalty management lives in the same dashboard: customer search, QR validation, point corrections. One interface for everything the staff touches daily.
Architecture
The tech decisions came from the requirements.
Convex as the backend. The kitchen dashboard and the order tracking page both need real-time data. When a customer submits an order, the kitchen sees it instantly. When staff mark an order as ready, the tracking page updates without a refresh. A traditional REST API with polling would have added latency and complexity. Convex provides real-time sync, server-side mutations, scheduled jobs, and built-in rate limiting in one system.
Zustand for client-side state. The cart needs to survive page refreshes and hold complex nested data: items, variants, add-ons, deposit fees, pickup preferences. Zustand with persistence middleware handles this without the overhead of a full state management library. Server state stays in Convex. Client state stays in Zustand. The boundary is clean.
Next.js 16 with React 19 for the frontend. Server components handle the static parts (menu, brand pages, legal). Client components handle the interactive flows (ordering, dashboard, loyalty). This split keeps the initial bundle small while the interactive pieces load on demand.
Resend with React Email for transactional messages. Six email types, all templated as React components, all matching the site's design language.
GSAP with lazy loading. Animation code is dynamically imported, only loads when needed. The ticker targets 60fps. Batching caps at 5 elements per trigger to prevent jank. GPU-accelerated transforms with force3D, cleaned up on completion to prevent memory leaks.
Catering
The backend is ready: 14-day date picker, morning and afternoon slots, delivery details, billing, quantity-based discounts, free delivery above a minimum threshold. Supports both delivery and pickup fulfillment.
The restaurant is finalizing the operational workflow before launch.
The invisible part
From the outside, this looks like a restaurant website with a blue color scheme and appealing food photos.
Underneath: time slot generation from live opening hours, deposit calculations per beverage, a five-state order pipeline with real-time sync, QR codes with 7-day expiration and staff-side validation, six-step email automation, perceived progress mapping on a circular tracker, rate-limited auth with account lockout, and confetti that fires when you earn a free burger.
The customer sees a menu and a button. The kitchen sees exactly what they need. Everything in between is infrastructure.
There's always a next level.
If you like what you see — whether you're building a product or a team — I'd love to hear about it.