Engineering Freshco’s Online Retail Platform for Web and In-Store Kiosks
How I built a single online platform that powers customer web ordering and tablet-based in-store kiosks, with real-time payments and automated receipt printing.

#1. From Pen-and-Paper to Automated Retail
When Freshco began its digital transformation, the goal wasn’t just to have a website. They needed to replace a fully manual retail workflow built on handwritten orders, manual receipts, and slow fulfillment with a system that could keep up with real-world store operations.
Every delay at the counter affected customer satisfaction. Every handwritten receipt introduced room for error. As a Frontend Engineer, my responsibility was to design a digital system that didn’t merely replicate existing processes, but actively removed friction from the physical store itself.
#2. The Core Challenge: Two Very Different User Journeys
Freshco operates in two realities at once:
- Online customers, who expect accounts, order history, and scheduled delivery
- In-store walk-in customers, who care only about speed
The challenge was to support both experiences from a single technical foundation, without duplicating logic or increasing operational complexity.
#3. User Flow 1: Online Storefront
Domain: freshco.id
This is the primary customer-facing experience.
- Users can register, manage profiles, and track past orders
- The focus is long-term retention and brand trust
- Stability and consistency matter more than raw speed
From a business perspective, this channel builds loyalty and repeat purchases.
#4. User Flow 2: In-Store Kiosk (Digital Signage)
Domain: signage.freshco.id
This flow is designed for tablets inside the physical store.
- No login
- No account creation
- Straight to ordering and payment
The priority here is transactional speed.
Every extra step increases queue time and staff workload.
#5. Architecture Decision: One Codebase, Two Runtime Paths
To avoid maintaining two separate applications, I implemented a single Next.js codebase with different runtime entry points.
Internally, the kiosk flow is identified using a base path (/ds), allowing shared UI and business logic while enabling a distinct “guest-only” experience.
#5.1 Why this matters (non-technical view)
This approach:
- Reduces development cost
- Keeps behavior consistent across channels
- Allows features to ship faster without duplication
#6. Deployment Strategy: Dual-Container Isolation
To ensure stability in a busy store environment, the application runs as two containers:
- Port A: Public storefront (
freshco.id) - Port B: High-frequency kiosk traffic (
signage.freshco.id)
An Nginx reverse proxy routes traffic based on domain, while Next.js rewrites handle internal navigation.
This setup isolates kiosk traffic, preventing in-store spikes from impacting online customers.
#6.1 Next.ts Rewrite Configuration
// next.config.ts
async rewrites() {
return [
{
source: `/ds/:path*`,
destination: `/:path*`
}
]
}#7. Real-Time Payment Feedback with TanStack Query
In a physical store, waiting for payment confirmation is not acceptable. Staff need to act immediately once a transaction is completed.
Instead of introducing WebSockets (which can be fragile on unstable store networks), I used TanStack Query’s controlled polling.
#7.1 Why polling instead of WebSockets?
- More resilient to intermittent Wi-Fi
- Automatic retry and error handling
- Easier operational debugging
From a business standpoint, this ensures the system reacts reliably, even in imperfect network conditions.
const { data } = useQuery({
queryKey: ["order_status", orderId],
queryFn: () => fetchOrderStatus(orderId),
refetchInterval: (data) => (data?.status === "PAID" ? false : 3000),
onSettled: (data) => {
if (data?.status === "PAID") {
printReceipt(orderId)
}
},
})#8. Automated Printing: Removing the Final Manual Step
The most visible impact of this system is the automated receipt printing.
Once payment is confirmed, the system:
- Checks printer availability
- Generates a receipt digitally
- Prints it automatically without staff interaction
This removes an entire manual step from the workflow.
#8.1 Automated Print Flow
For store staff, this means:
- No typing
- No printing delays
- Fewer mistakes
#9. Measured Business Impact
| Metric | Before | After |
|---|---|---|
| User Onboarding | Assisted manually | Zero-login kiosk flow |
| Receipt Generation | ~60 seconds | Instant |
| Order Processing Time | 5–7 minutes | < 2 minutes |
| Operational Errors | Frequent | Significantly reduced |
| System Stability | Single runtime | Isolated containers |
#10. Final Takeaway
This project reinforced a core belief of mine:
Frontend engineering isn’t just about interfaces. It’s about designing systems people can rely on in real-world operations.
By combining thoughtful architecture, pragmatic trade-offs, and seamless hardware integration, I helped Freshco transition from a manual workflow to a scalable, digital-first retail platform while preserving speed on the shop floor.