• Accueil
  • Fonctionnalités
  • Tarifs
  • Comparatifs
  • Contact
FR

Plateforme F&B — Gratuit, Complet, Simple

Produit

  • Accueil
  • Fonctionnalités
  • Tarifs
  • Comparatifs
  • Contact
  • Aide & Documentation
  • Nouveautés

Entreprise

  • À propos
  • Blog
  • Carrières
  • Presse
  • Partenaires
  • Créer votre app

Mentions légales

  • Conditions d'utilisation
  • Politique de confidentialité
  • Politique de remboursement
  • Politique des cookies
  • RGPD

Nous contacter

  • cro@prionation.io, ceo@prionation.io, consult@prionation.io
  • WhatsApp
  • Canggu, Bali
  • Nous rencontrer
Infrastructure parPRIONATION.io
© 2026 Epidom. Tous droits réservés.v3.3.0
Conditions d'utilisationPolitique de confidentialitéPolitique de remboursementepidom.fr
Nouveautés

Les nouveautés d'Epidom.

Toutes les améliorations, corrections et nouvelles fonctionnalités, livrées rapidement et documentées ici.

Les notes de version sont rédigées en anglais.

3.3.0 — 27 septembre 2026Fonctionnalité
  • Set up your store in three steps. Setup now asks for what matters first: your store's name, country, city and what kind of place it is. Then your storefront, with your logo, colour, a short tagline and your first three items, next to a live preview. Last, what you want Epidom to help with. Each step is saved as you go: close the page and you pick up where you left off, with a Back button on every step.
  • Your country sets up the rest. Choosing your country sets your store's currency, payment methods, delivery platforms, time zone and language. A store in France now starts in euros, with card, cash, cheque, bank transfer and Titre-Restaurant, and Uber Eats, Deliveroo and Just Eat at the till. Before, every new store started in Indonesian rupiah and on Jakarta time. Prices you type during setup are in your own currency.
  • Fill it in from Instagram. A screenshot of your Instagram profile can still fill in your name, tagline, logo and colour. It's now a shortcut on the first step instead of a screen of its own.
  • Your store is live. When you publish, you get your store link, a QR code to print, a WhatsApp share button and a one-minute tour.
  • A tour of Epidom's three spaces. A short tour explains the storefront, POS Mode and the Back Office, which plan each part needs, and how to move between them. You can replay it from Help.
  • A setup checklist on your dashboard. New stores get a "Get your store ready" list that ticks itself off as you go: publish, add items and photos, add your logo and WhatsApp number, get your first visitor. On the right plan it adds your first sale, shift, tables, stock, suppliers, staff and schedule. Parts your plan doesn't include say what they would add. Hide the list whenever you like.
  • Help inside the app. A new Help page, at the bottom of the sidebar and under Help & what's new in POS Mode's menu, has the guides, the latest updates, WhatsApp support and a button to show page tips again. Four new guides cover POS Mode, stock and supplier orders, staff and schedules, and printers and the scanner. They're also on the website's Docs.
  • Page tips. The first time you open Stock, Schedule, Shifts, Finance, Customers, Data, the storefront editor, the Order Queue, Kitchen & Bar, Tables, Operational or Hardware settings, a short card explains what the page is for. Got it hides the card.
  • Creating another store starts with what matters. The Create a store dialog now asks for the name, country and city first, and can copy the currency and payment settings of one of your other stores. The photo, address, phone and email are under More details. A new store now gets its payment settings, a draft storefront and you on its staff list straight away, instead of starting in rupiah. When you reach your plan's store limit, the button and the message name the plan that allows one more.
  • Set your business time zone. Profile → Business info now has the time zone used for attendance, shifts and reports. It used to be fixed to Jakarta.
  • Signing up takes you to setup. After you confirm your email you go straight to setup, instead of the website's home page, and the "check your email" page opens instead of sending you to the login page. New Google accounts go to setup too.
  • Security. An old card-check promotion that could still grant the POS plan for free is removed. The scrambled copy of the owner PIN is no longer sent to the browser, and guessing the owner PIN or its reset code is limited to a few tries a minute. Removing an uploaded image now only ever deletes your own files.
  • Image uploads explain themselves. When a logo or photo can't be uploaded (too big, wrong type, no connection), the upload box now says why, in your language. Replacing an image keeps the old one until the new one has uploaded, so a failed upload no longer leaves a broken picture.
  • Clearing a store's details works. Emptying the address, phone or email in Edit store now clears it. Before, the old value quietly stayed.
  • The Order Queue opens on the current shift. While the till is open, Active and History start with every order since the shift was opened, storefront and delivery orders included, and Back to current shift returns there. With no till open they open on today, as before.
  • Removed. The AI logo generator, which only ever produced a placeholder, and the AI menu suggestions, which only knew Indonesian dishes. Setup is now fully translated into French and Indonesian.
3.1.2 — 27 septembre 2026Correctif
  • Card payments on your storefront go to your own Stripe account. If your store hasn't connected Stripe yet, customers are asked to pick another payment method instead of the card payment landing with Epidom. Card totals are also sent at the right amount.
  • A plan you switch in the billing portal stays switched. Your plan now follows the price you actually pay, and refreshing your billing no longer marks a trial or an overdue subscription as cancelled.
  • Subscribing again replaces the old subscription completely. An overdue or trial subscription you replace is cancelled, so you are never billed twice. A late payment on a subscription that has ended no longer reopens it, and a delayed or repeated notice from Stripe can no longer cancel the subscription you have now.
  • Your billing record stays linked. A brief Stripe outage can no longer detach your account from its billing history.
3.2.0 — 26 septembre 2026Fonctionnalité
  • Financial reports on Operations. Finance, with its P&L, margins, fees and cash reconciliation, is now part of the Operations plan instead of Enterprise. It's where your recipe costs and multiple outlets pay off.
  • All your outlets in Finance. The Owner dashboard is now the All outlets view of Finance: switch between This outlet and All outlets at the top of the page. Each outlet shows revenue, gross profit, margin, waste and net profit for the same dates, with a total row, an Excel export and a link to that outlet's full report. Old Owner links open it.
  • Outlet figures match their own report. An outlet's row in All outlets now shows the same net profit as its own Finance page, after refunds, tax and card fees. When your outlets use different currencies, each one is shown in its own currency and money isn't added across them.
  • Full report from the dashboard. The dashboard's Analytics section links to Finance for the same dates.
  • Operations covers up to three outlets. A fourth outlet needs Enterprise. Your Billing page shows how many stores your plan allows and which plan allows more. Taking over a store from another owner counts too.
  • Enterprise is for chains and custom builds. Enterprise covers four or more outlets, your own system designed and built with our team on Prionation, custom integrations (accounting, delivery apps, payments) and dedicated support. Talk to us on WhatsApp. Custom Development requests are now open on every paid plan.
  • Compare plans without leaving the app. Billing now lists POS, Operations and Enterprise with their price, what each one includes and a Monthly / Yearly switch. You no longer need to go to the website.
  • Change plan from the card. Upgrade, downgrade or start the 14-day POS trial from Billing: new subscriptions go to Stripe to pay, and subscriptions you already pay for open Stripe's billing page. Enterprise opens a WhatsApp chat with us.
  • Prices you'll actually pay. Plans show the exact price in your store's currency (euros, dollars or rupiah) instead of a converted estimate.
  • Unlimited stores say so. Store usage on an unlimited plan now reads "2 / Unlimited stores" instead of "2 / stores".
  • Database backups on demand. Admins can start a backup from the Capacity page with Run backup now, and watch it finish in the backup history, instead of waiting for the nightly job.
3.1.1 — 26 septembre 2026Correctif
  • Your plan switches on when you pay. After checkout, POS or Operations now turns on straight away. Before, Epidom couldn't read Stripe's current payment notices, so a successful payment could leave the account on Free.
  • Missed and recovered payments are tracked. A declined renewal now marks the subscription as past due, and paying it puts it back to active.
  • Cancellations you schedule stay scheduled. Choosing to cancel at the end of the period now shows on your Billing page until the period ends.
3.1.0 — 25 septembre 2026Fonctionnalité
  • POS System and Operational. The More menu now opens POS Mode's two spaces. POS System is the till with its four tabs (Cashier, Orders, Kitchen & Bar, Tables). Operational puts your shift, your schedule, the team's schedule and clocking in on one page, without the bottom tabs.
  • Team Schedule in POS Mode. Owners and managers see the week's roster and schedule photos published in the Back Office, read-only, with previous and next week. Drafts never show.
  • Clock in and out on the page. Clocking in is a tab of Operational instead of a pop-up, with Back buttons on every step.
  • Hardware settings. Printers and the barcode scanner for this device, in one place from the More menu. You can test your scanner from any POS screen.
  • A tidier More menu. The sync state sits right under Sync sales, the customer display's open-window button sits beside its switch, Send feedback moved down, and the printer button only shows on the till screens.
  • New customers save themselves. When a new customer finishes on the customer display, the till saves them and adds them to the sale. The cashier no longer taps Save.
  • Typing feedback on every on-screen keypad. Keys press and ripple on the PIN pads and on the customer display's keyboard and number pad, and typed characters pop in.
  • Your Stores, redone. Each card shows the store's storefront cover, logo and colour, its slogan, currency, total revenue, customers, staff and market. Tapping a store asks whether to open the POS System or the Back Office, picking up where you left off. The POS Cashier shortcut on the card is gone.
  • Signed-in pages stay in the app. On Your Stores and Profile the logo no longer leads to the website and the back arrow is gone. Log out to see the website.
  • More French. Clocking in, My Schedule, the Team Schedule and the More menu's account options are now in French.
3.0.0 — 24 septembre 2026Fonctionnalité
  • POS Mode is for the counter. Cashiers and kitchen staff get the till, orders, the kitchen and bar display, the customer screen, tables and clocking in, with large buttons and no management pages in the way.
  • The Back Office is for running the business. Owners and managers get stock, the menu, staff and schedules, finance, the storefront and settings, grouped by task. Enterprise stores can see all their outlets side by side without leaving the Back Office.
  • The till is rebuilt. It has a live receipt-style bill, customers and loyalty points, discounts, split and merged bills, online delivery orders, up to four printers and a screen facing the customer.
  • One shift for the whole store. Every cashier and device shares it, and past shifts are kept in a shift history.
  • Staff sign in with their own account and go straight to their store's till.
  • The website is available in English, French and Indonesian on every page, including the legal pages.
  • Compare is in the website's top menu, between Pricing and Contact, and stays highlighted on each comparison page. On laptop screens the menu no longer runs under the logo or squeezes the Try Epidom button onto three lines.
  • Meet us. A new button at the bottom of every website page opens Prionation's booking page, where you can set up a meeting with the team.
  • Our address is now Canggu, Bali at the bottom of every website page, in every language.
  • A new logo. The grocery-bag icon and the ÉPIDOM wordmark replace the old square icon and lettering on the website, the sign-in page, the Back Office top bar and the till's menu. Printed reports, the customer screen and staff invitations show the new wordmark.
  • Start Trial. The button at the top right of the website now says Start Trial instead of Try Epidom.
  • The Status page is gone. It was never connected to live monitoring. Old links to it now open the Contact page, where you can report a problem.
  • The Features page describes Epidom 3. It opens on the three spaces (the storefront, POS Mode and the Back Office) and now covers what the last releases added: customers and loyalty points, the customer screen, up to four printers, one shift for the whole store, rosters and clock-in, supplier orders and the stock log. Each feature names the plan it needs. Claims the product doesn't back up are gone: allergen and nutrition labels, tax-authority exports (FEC), auto-translated menus, next-day payouts, story-ready product cards and reorder alerts by WhatsApp. The sample bills, recipes and reports now show euros in French, rupiah in Indonesian and dollars in English.
  • Footer tidy-up. "Build Your Own" now reads "Build your app", and the Prionation credit shows its name without the icon.
2.104.0 — 24 septembre 2026UX
  • Item | Delivery Order. The tabs at the top of the Stock page are now Item and Delivery Order. Delivery Order holds what was under "Reorder & Deliveries", so the Items / Reorder & Deliveries switch under the title is gone.
  • History is now the Log. It has its own tab, set apart on the right of Item | Delivery Order.
  • Grid, Columns or List. A switch next to Select All changes how items are laid out, like the till's menu. Grid shows full cards, Columns shows smaller cards with more to a row, and List shows one row per item. Cards are as tall as their content. The Stock page remembers your choice on each device.
  • Details in a pop-up. Tap an item to open its details: stock information, expiry date, and the Adjust Stock, Record Waste, View History and Reset to 0 buttons. The checkbox on each item still selects it for Bulk Adjust without opening it.
  • The reason is optional when adjusting stock. Adjust Stock and Bulk Adjust no longer ask for a reason before saving. Leave it blank and the adjustment is recorded without one.
  • The Log records every stock change. Stock set when adding or editing a product, and stock brought in by a CSV or Smart Import, now write a Log line, as materials already did. Materials used in production show as a reduction, not an increase.
  • More in each line. Each line says what happened (Sale, Delivery, Produced, Used in production, Adjustment, Waste, Returned), the reason, the time (now on phones too) and the stock left afterwards.
  • The whole history. Load more goes back through everything. Search and Oldest first now cover the whole history, not only the last 50 lines. A date range now includes its last day.
  • Re-importing products keeps their stock. A product sheet without a stock column used to reset every product it matched to 0. Now their stock is left as it is.
  • Bulk Adjust works. It used to open with "No items selected" and could not be saved, which also blocked Adjust Stock for products. It now opens with the items you picked.
  • Adjust Stock works for every material. It only knew the 50 newest materials, so saving an older one did nothing. It also showed the previous material's name when opened for another one.
  • Till fixes. Switching store from the More menu with items in the cart no longer freezes the till behind the menu. A table picked for a saved bill is marked free again once the bill is paid without the kitchen display.
  • Finance reports show Just Eat's commission as 14% instead of 14.000000000000002%.
2.103.0 — 24 septembre 2026UX
  • Creating an order places it. The "Orders to Place" list and its "Mark as Placed" button are gone. A new order goes straight to Awaiting delivery. From the "Order created" screen you can print it or send it to the supplier by email or WhatsApp.
  • The status updates itself. Each open order shows "Arrives tomorrow", "Due today" or "Late by 3 days", worked out from its expected delivery date. There is no status to pick any more. Late orders are listed first.
  • One tap to receive. Received shows what goes into stock ("Flour +5 kg"), and Yes adds it and records today as the received date. There is no date to pick. Not yet leaves the order in the list, where it turns late on its own.
  • Expected delivery defaults to tomorrow in the create and bulk-order dialogs. You can still change it.
  • Other actions are in the ⋯ menu: send to supplier, print, change the date or notes (for a delivery the supplier pushed back), and cancel an order that will never arrive.
  • Delivery history (received and cancelled orders) is folded away under the list. Tap it to open.
  • Stock can't be added twice. A double tap or a second device on Received used to be able to add the same delivery to stock twice. Now only the first tap counts. Receiving also no longer overwrites stock that a till sale changed a moment earlier.
2.102.0 — 24 septembre 2026UX
  • More sits next to your name, top right. It is the Epidom logo, right after the staff name button, with no gap between them. The bottom tab bar now holds only the till's pages.
  • The name button shows the name only. It no longer repeats the role ("Owner · OWNER"). On a narrow phone a long name is shortened instead of pushing the buttons off the screen.
  • A side menu instead of a pop-up. It slides in from the right: 40% of the screen on a tablet or computer, most of the screen on a phone. The Epidom logo heads it, above a card showing who is signed in: name, role, store and, on your own account, your email.
  • Sync sales, and the sync state at a glance. A Sync sales button at the top sends any sales saved while offline and refreshes this device's copy of the menu and orders. A strip along the bottom says "All sales synced", how many offline sales are still waiting, or that the till is offline.
  • One row per action. Shift, My Schedule, Clock In / Out, Send feedback, the customer display, language, dark mode, zoom and the account actions each have their own row. The page you are on is highlighted.
2.101.0 — 24 septembre 2026Fonctionnalité
  • Dine In | Take Away | Others. A third option sits next to Dine In and Take Away. "Others" lists the delivery platforms of your market: GoFood, GrabFood and ShopeeFood in Indonesia; Uber Eats, Deliveroo and Just Eat in France; Uber Eats, DoorDash and Grubhub elsewhere. "Other platform" is always there for anything else. The market is the one set in Back Office → Fees & Taxes. Pick a platform to key in an order that came through it.
  • Each platform in its own report row. Platform orders are recorded against their platform. The daily and shift report's "By sale type" block gets a line per platform (GoFood, GrabFood, …). The finance Channels report and its channel filter count them with the platform's commission taken off. Receipts, kitchen tickets, the order queue and order history name the platform too.
  • Pax, table and customer in one dialog. The pax chip left the order-type row. Tap "Add Customer" (or the attached customer) to set the number of guests, with − / + in the middle and a row of numbers 1–30 to tap underneath. The same dialog holds the table and, optionally, the customer. The row shows the pax and table at a glance.
  • Tables from your Tables page. The table field is a dropdown of the tables you registered, with their seats and status. Picking one links the order to that table and marks it occupied. "Custom…" still lets you type any table that isn't registered.
  • The customer can type their own details. The customer section has the Customer display switch and an "Ask customer for details" button. It opens the WhatsApp number form on the customer screen. A returning customer is added to the sale automatically; a new one fills the form in the dialog for you to save.
2.100.0 — 24 septembre 2026UX
  • Food / Drink tabs replace "+ Add filter". All, Food (burger icon) and Drink (wine-glass icon) now sit next to the search box on the till and are always on screen, plus a tab for your second product line if you run one. Food lists the items whose Department is "Kitchen / Food" and Drink the "Bar / Drink" ones. An item made in both the kitchen and the bar appears under both.
  • Categories are cards. The menu opens on one card per category, with its item count. Tap a card to see its items, with their names, prices and photos. A Back card sits in the top-left corner, and a "Categories › Drink › Coffee" trail runs above them. Changing tab takes you back to that tab's categories. A menu with a single category opens straight on its items.
  • Search still covers the whole menu. Typing or scanning looks through every category of the current tab at once. Clear the search to go back to where you were.
  • On a phone, the cart button floats and the search row is shared. The cart button is a floating circle at the bottom right, just above the tab bar, instead of a row of its own at the top. The search box and the Food / Drink tabs share one row, with Food and Drink shown as icons.
  • Department is explained in the Back Office. When you add or edit a product or a storefront menu item, a note under the Department field says it chooses the till's Food or Drink tab as well as the kitchen or bar ticket. An empty Food or Drink tab on the till points you to the same setting.
2.99.0 — 20 septembre 2026UX
  • Privacy, Cookie and GDPR pages — rewritten and translated. They were English-only. They now exist in French, Indonesian and English, on the same layout as the Terms and Refund pages, and describe what Epidom really does: which providers handle your data (payments, email, hosting, nightly backups, analytics, messaging), what is collected — including staff attendance photos and location — how long it is kept, and how to use your rights. The old "EU servers (Frankfurt)" and "Data Protection Officer" claims are gone, and the French Terms now include the account-deactivation and data-retention clause.
  • Partners, Careers, Press, Status and the public Changelog are translated. Changelog dates follow your language, a note says release notes are written in English, and "Back to Home" keeps you in your language.
  • Search results and link previews match the page language. Titles, descriptions and share cards on the marketing pages appear in French, Indonesian or English instead of always English. The pricing snippet shows the price in your currency, the home snippet no longer calls the cashier free (the storefront is free forever; the POS has a 14-day trial), and the server-rendered page now declares its language.
  • Each language version points search engines at itself. /id and /en pages are no longer treated as copies of the French page, French blog posts and guides no longer advertise English and Indonesian versions that don't exist, comparison pages say which language they really serve, and two unsupported claims (a founding year, a site-search box) are gone from the structured data.
  • Sitemap dates are real. Articles report their publication date; pages with no known date report none instead of the moment the sitemap was built.
  • The old /payments page now redirects to /pricing in every language.
  • Removed the unused waitlist form, which sent visitor details to a third-party server.
  • Plan prices and the support contact are each defined in one place, with tests that fail if a translated price or a hardcoded support address drifts.
  • Footer copyright, "Infrastructured by", screen-reader labels and the 404 page links follow your language. The account deactivation and reactivation settings are translated into French, and attendance address lookups now identify themselves with the real support inbox.
2.98.0 — 20 septembre 2026Fonctionnalité
  • Pricing has its own FAQ. What happens after the 14-day trial, changing plan or cancelling, and refunds — with a link to the refund policy — instead of the homepage FAQ repeated.
  • Docs guides end with a "Start free" card, and blog posts have a byline. The card links to sign-up in French, Indonesian and English. Posts show "Epidom Team" with the date and reading time; a named author with a photo can be added per post later. Both sign-up buttons record a click.
  • Partners: a supplier application path. Coffee roasters, frozen-food distributors and bakery suppliers pick what they supply and open a prefilled WhatsApp message (or email us).
  • Careers and Press have real buttons — "tell us about yourself" and "Media inquiries" — instead of email addresses to copy by hand.
  • The home trust bar shows what Epidom works with (Stripe, Xendit/QRIS, WhatsApp) instead of shop names.
  • Ready for real proof, invisible until it is real. Customer logos, case studies and team cards are now data-driven sections that stay hidden until real, sourced and consented entries are added. The About page shows an honest "small product team" block instead of placeholder avatars and a numbers card.
2.97.0 — 20 septembre 2026Correctif
  • Tighter plan activation. The free-plan activation now only ever activates the Free plan, and the service behind it refuses anything else.
  • Signing in only follows links inside Epidom. A crafted ?next= link can no longer send you to another site after you log in.
  • Your email stays out of the page address. The sign-up box on the home, services and pricing pages used to put what you typed into the URL, where analytics and server logs could see it. It now carries it to the sign-up form privately, keeps your place when you flip between Log in and Create account, no longer claims a "magic link" is on its way, and stacks on phones so the French button no longer squeezes the field.
  • Sign-up and login forms are fully French and Indonesian — the "or continue with email" divider, the buttons and every validation message. Passwords must be at least 8 characters on sign-up (what the server always required); signing in no longer checks length.
  • Pricing in French, and honest. The trial badge, note and button are translated; the trial banner no longer says "no card" (a card is required, and the trial is POS-only); French feature lists match the real plans; the Free card no longer lists POS and KDS; the plan-change dialog and its errors are translated in all three languages; only POS is marked "Most popular"; the Operations button no longer says "Start free trial"; and customers already on a paid plan are no longer offered the trial.
  • The pricing sign-up path remembers your plan. A signed-out visitor who picks a plan goes straight to sign-up and comes back to the pricing page (POS reopens the free-trial dialog), and the plan-change dialog works from the keyboard — focus moves in, Tab stays inside, Escape closes.
  • Unverified numbers, testimonials and claims removed. "500+ businesses", "20+ countries", "10k orders a day", a 4.9 rating, made-up customer quotes with outcome statistics, and invented shop names are gone; the hero shows four real product facts instead. The home, services and About pages no longer promise menu-photo import, an official WhatsApp API, WhatsApp pings, daily reports emailed at midnight, or "5-minute setup" — they say what Epidom actually does.
  • Contact reaches us. WhatsApp is now the main way, with a message already written for you (France and Indonesia numbers, matched to your language). The contact form, which never sent anything, is gone, and so are an unsourced "under 4 hours" reply time and Indonesia-only support hours.
  • Cookie choices are real. A "Manage cookies" button in the footer lets you change or withdraw your choice at any time. Google Analytics and the Meta Pixel are not loaded at all until you accept them; withdrawing stops them and deletes their cookies; the Meta image fallback, which could not honour consent, is removed; switching language no longer counts as refusing cookies; and the banner says plainly what Accept turns on and links to the Cookie Policy.
  • The Cookie Policy no longer says things that were false ("self-hosted analytics", "no third-party trackers", "no advertising cookies"). It lists what actually runs, including that, if you accept analytics, Google Analytics also receives in-app page addresses, some in-app actions and the details of a sale rung up at the till.
2.96.0 — 20 septembre 2026Fonctionnalité
  • Zoom on the till. The More menu in POS Mode now has a Zoom row next to Language and Dark mode, so a cashier can fit more of the queue on a small counter screen, or enlarge everything for a kitchen display, without leaving the till. Until now zoom only existed in the Back Office account menu.
  • Zoom in the Back Office menu on a phone. The menu that slides in from the right now has a Zoom control alongside Language and Theme — where you'd look for it on a phone. The account menu still has it too.
  • A phone can't be zoomed into a broken layout. Zooming in leaves the page less room to lay out in, and a phone has little to spare: at 125% or 150% the page was laid out narrower than the app is built for, so content could run off the edge. A phone now zooms out freely (70–90%) but stops zooming in once the page would get too narrow, and the control says so — "Screen too narrow to zoom in further". Tablets and desktop keep the full 70–150%.
  • Your choice is kept. Turn a tablet to portrait and the zoom steps down to what fits; turn it back and your 150% returns. The saved setting is never overwritten just because the screen was narrow for a moment.
  • Fix: a few sizes ignored the zoom. The slide-in menu's width, and the "no orders" and "no tables" screens on the till, were sized against the physical screen rather than the zoomed page, so at 70% or 150% they came out too narrow, too wide or too short. They now follow the zoom like the rest of the layout.
2.95.0 — 20 septembre 2026Fonctionnalité
  • Give your number on the customer screen. The customer types their WhatsApp number on the pad; the till checks whether they're already a customer. A returning customer is greeted by their first name and is attached to the sale automatically — no name or email asked for. If you've already picked a customer yourself, that choice is never overwritten.
  • New customers can leave a name and email — both optional. A number the till doesn't know opens the New customer form on the cashier side with the number already in it, then fills in the name and email live as the customer types them on the customer screen's on-screen keyboard. You review it and tap Save & attach — nothing is saved from the customer's side. Anything you type yourself in a field is left alone.
  • The New customer form is WhatsApp-first. The WhatsApp number comes first, and name and email are optional — a number alone is enough (the customer is named after their number until you rename them in Back Office).
  • Receipts email themselves. When an order is paid and the customer left an email — on the customer screen, or on their customer record — their receipt is emailed to them automatically. No extra tap.
  • Is the receipt emailed? Now you can see. The Sale complete screen and an order's details in Order Queue → History now say whether the emailed receipt went out — sent (to which address, and when), failed (and why), on its way, or not sent yet — and offer Resend once it has been sent.
  • Fix: History said "sent via WhatsApp" for emailed receipts. The receipt line under an order showed the last send, whichever way it went, as "Receipt sent via WhatsApp". WhatsApp and email each have their own status now.
2.94.0 — 19 septembre 2026Fonctionnalité
  • One shift per store. A shift now belongs to the store, not to whoever opened it. Open it as the owner and every cashier who signs in — on that tablet or another — sees the same running shift, and their sales count against it. The Shift page says Started by the person who opened it, so a cashier taking over no longer sees "No active shift". A second shift can't be opened while one is running, and if two people race to open or finish one, the loser is told it's already open or already ended instead of "try again". Anyone signed in to the till can finish a shift someone else opened. If your store already has several open shifts from before this change, the newest shows first; finish it and the next one appears. Reports name who opened a shift, not who rang each sale or who closed it — that's how a shared shift works for now.
  • Shift history. Under the current shift on the Shift page: every finished shift, newest first — who ran it, how many orders, the cash counted and whether the drawer balanced. Tap one to open its full report.
  • Shifts, in the Back Office. A new Shifts page, next to Schedule, is the manager's shift report. For any period, and one person if you like, it lists each shift with its opening cash, cash sales, expected and counted cash and the difference — shifts that don't balance are in red, each with a link to its full report. A Cash log tab lists every cash in and out with the reason it was recorded; a shift's opening float and closing count are labelled as such, not as cash in and out. Managers have the page, and anyone who could see till cash on the Schedule page keeps that view — managers with a saved page list that includes Schedule are given Shifts too.
  • Attendance is only attendance. Log & History on the Schedule page no longer mixes till cash into the clock-in timeline. Opening cash and closing counts moved to the Shifts page.
  • Schedule image. On the Work Schedule page, choose Shift blocks (the grid, as before) or Schedule image: upload a photo or screenshot of your schedule for the dates shown, and every staff member sees it on their My Schedule page as soon as it's uploaded. Upload again for the same dates to replace it, and add a short note for the team if you want. Your choice is remembered on that device.
  • A sale can't land on a shift that's already finished. If a tablet hasn't noticed yet that the shift was finished on another tablet, its next sale now goes to the shift that is running, or is left unlinked if none is — it used to be filed on the finished shift, which threw off that shift's cash and made the next drawer read over.
  • The Shift page keeps your count. A brief connection drop while you're typing the counted cash no longer replaces the page with a "couldn't load" message and wipes what you entered. Signing in as another cashier no longer briefly shows "No shift" when one is open.
  • Fix: buttons cut off at the end of a shift. In the end-of-shift dialog the Print button was clipped at the left edge. The buttons now wrap instead of overflowing, Print is shorter, and Copy link is just an icon.
2.93.0 — 19 septembre 2026Fonctionnalité
  • Switch store. The More menu now shows the store you're in. Tap it to see your other stores and jump straight to that store's till — not its back office. If a sale is in progress you're asked first, because it belongs to the store you're leaving and would otherwise follow you into the next one; use Save Bill beforehand if you want to keep it. Staff who sign in with their own account see the stores they work at, and a store where their role has no till screen is greyed out.
  • Language. One tap between FR, ID and EN.
  • Dark mode. A switch for light and dark — the whole row is the tap target, not just the small switch.
  • Fix: Order Queue scrolls again. On the Order Queue page, both the Active and History tabs ran past the bottom tab bar and couldn't be scrolled, so orders further down the list were unreachable. The page now scrolls.
  • Scan barcode button, now inside the search box. Tap it to open a panel with a Test field — scan any barcode to see whether the scanner works, whether your menu has that code, and how slowly the scanner sent its keys, without adding anything to the sale. Below it, two settings kept on that tablet: Scan anywhere on this screen (turn it off and a scan only counts when typed into the search box) and Scanner speed — choose Slow / Bluetooth if scans are sometimes missed. Not yet tried with a real hardware scanner.
  • Menu view options moved. The grid / columns / list switch is no longer in the top bar; it now floats in the top-right corner of the menu, over the tiles, and stays there as you scroll — it no longer takes a row of its own.
  • Charge has a menu button beside it. Discount and Reprint Last are no longer separate buttons above Save Bill — open the ☰ button to the right of Charge and pick them, along with split, merge, coupon and the rest, from the More menu. Reprinting the last receipt is now two taps instead of one. The Add Customer row shows a person icon.
  • Flatter top bar. The search box and its scan button, Add filter, the printer button and your name button in the top bar are now square and run the full height of the bar with no gap above or below, and show a pointer cursor. In the top bar, Add filter lost its dashed outline and is now just the label until you hover it; the Add filter buttons on the Order Queue and Order History filter rows are unchanged.
  • The Order Queue opens on today. On both the Active and History tabs, the orders you see now start as today's — everything placed since midnight on your own clock — instead of every order there has ever been. Change it from the date box next to Unpaid (Yesterday, Last 7 days, All time…) and tap Reset to today to come back. The date box is always there, so you can always see why an older order isn't listed. The POS / Online ordering counts and the Unpaid count cover the same day, and the "orders unpaid" alert opens All time so none are missing. Everyone gets this once, whatever they had filtered before; their other filters are kept.
  • Fix: "Today" starts at your midnight. In History, "Today" and every other date range began hours after midnight for anyone east of UTC — around 07:00 in Indonesia — so an order rung up in the small hours landed under yesterday. Days now run 00:00–23:59 on your own clock, and a "Today" left open past midnight moves on to the new day by itself.
  • Light mode is navy, not grey. Buttons, the Charge button, the active menu-view switch and the POS Cashier / Back Office links were a neutral dark grey in light mode; they are now the deep navy dark mode is built on, with cream text. Text, muted text and borders take a navy tint too — muted text is easier to read — and the dim behind pop-ups is navy instead of black. Dark mode is unchanged.
  • The Active Queue switch has room. On a narrow screen the switch on the Order Queue page no longer sits against the edge of the screen.
  • Switch Account shows who's signed in. When you open Switch Account, the person already signed in on this tablet — or Continue as owner, if that's you — is highlighted in amber, and tapping it takes you straight back with no PIN to re-enter.
  • Shift label matches the connection label. The "No shift" / open-shift label in the top bar is now the same compact size as the green Connected label. Its tap area is unchanged.
  • Fix: signed out means back to the login screen. Opening Your Stores after your session had expired or ended showed "Failed to load stores — Unauthorized" with a Try Again button that could never work. You are now sent to the login screen instead — both when you open the page and if your session ends while it is already open. Other pages already did this.
2.92.0 — 19 septembre 2026Fonctionnalité
  • Printers, in the top bar. The printer button now lists every printer in use with its own Connect button, and Printer settings opens one card per printer: switch it on, pair it, choose 58mm or 80mm paper, choose whether it prints by itself, and press Test print to see its real layout on paper. The icon turns amber when only some of your printers are connected, so a dead kitchen printer can't hide behind a healthy receipt one.
  • Receipt printer (main). Cashier to customer: the receipt with prices and total, the bill and the shift report — unchanged. A till that already had auto-print or a paper size chosen keeps both.
  • Kitchen and bar order tickets. Items, options and quantities in large bold type, with the table, order type, guests, the call-out number and any notes — and never a price. Each item goes to the printer for its area, using the same Kitchen / Bar setting the kitchen screen already uses. If only one of the two is set up it prints everything, in a section per area. A custom item with no area prints nowhere, exactly as on the kitchen screen.
  • Item labels. One sticker per item ordered — two coffees are two stickers, "1/2" and "2/2" — with the call-out number, item, options, note and time. Pick your printer's language (ESC/POS or TSPL), the sticker size and gap, and whether stickers are for all items, kitchen items or bar items.
  • It prints when the order is placed — the same moment the order reaches the kitchen screen. A printer that should have printed but isn't connected is never silent: a warning names it and offers Connect & print, and a print that fails partway offers Try again. A printer that stops responding is disconnected and named instead of freezing every other printer. On the send-receipt screen, Print order tickets prints a second copy, marked REPRINT.
  • The call-out number on paper. Tickets and labels carry the order's #12; the server now returns it when an order is created.
2.91.0 — 19 septembre 2026Fonctionnalité
  • A new Google Reviews tab in Storefront. Paste the review link from Google Business Profile ("Get more reviews") or your Google Place ID, and Epidom checks it and sets up the review button for you. You can test the link, download a QR code to print for tables, the counter or receipts, pause the button any time without losing the connection, or disconnect.
  • It shows up on your storefront by itself. Once connected, a Review us on Google button appears on your public storefront page, and your Google Maps link is added for you — no second field to fill in (a Maps URL you entered yourself still takes priority). When an order is delivered, the customer's order page asks for a review too.
  • See whether it works. Storefront → Analytics has a new Google Review taps card counting taps on the button.
2.90.0 — 19 septembre 2026Fonctionnalité
  • A Shift page of its own. Opening a shift, watching it and ending it moved out of My Schedule (which keeps clock in/out, your roster and your history, in POS Mode and in Back Office) into Shift, in the More menu. With a shift open it shows who is on charge, when it started, the opening cash, and a red Finish shift button, with Cash in / out and a live shift report beside it. With none open it asks for the opening cash. The account owner works a shift as the store's owner profile.
  • The shift label in the top bar. A green Shift · 18:00 while your shift is open, an amber No shift when it isn't; tap it to open the page. Kitchen accounts, who never count cash, don't see it.
  • Finish shift. The cash the till is accountable for, line by line — opening cash, cash sales, refunds, tips, float top-ups, paid out, safe drops, tips paid out — down to the expected cash, then the amount you actually counted and the live difference (balanced, over or short). Beside it: when the shift started, how many orders it took, every other payment method with its order count and total, and a note for whoever opens next. The count is typed blind: it is never pre-filled with the expected figure, and End shift stays off until you enter one. Back leaves the shift open.
  • Confirm, then the report. Ending asks first, showing expected cash, counted cash, the difference, other payments and your note; it can't be undone. Afterwards the shift report is printed on the receipt printer when one is connected, previewed on screen exactly as it prints, and reachable by link (Copy link, Open report). With no printer connected nothing pops up — Print report pairs one, or on an iPad (no Bluetooth printing) opens the browser's print dialog.
  • Also fixed on the way. Sales rung up after a shift was opened, without signing in again, were not attached to it — so their cash was missing from the drawer's expected total — and sales after a shift was closed kept attaching to the closed one. The till now follows your real shift on every POS screen.
2.89.0 — 19 septembre 2026Fonctionnalité
  • A live bill on the right, with the order queue one tap away. The cart is now a receipt that updates as you ring up — items, discount, sub-total, service charge and tax with their rates, total. An Order Queue button with a live count jumps to the queue, and a Dine In / Take Away switch (with guests and table) lives on the bill itself, so Save Bill and checkout inherit it instead of asking again.
  • Save Bill, Print Bill, Reprint — always in reach. "Hold order" is now Save Bill (a rename: bills already held work exactly as before). Print Bill prints a provisional bill that says it is not a receipt. Reprint Last stays visible, and a More sheet holds the rest: Custom Item, Split Bill, Merge Bill, Discount, Coupon, Redeem Points.
  • Add a customer without a pop-up. A row at the top of the bill expands in place: search as you type, or add someone new, or leave it empty. The chip shows their points and lifetime spend. A new Customers page in Back Office lists everyone with customer-since, member-since, points, life spending, orders and last visit, exports to CSV, and opens each customer's orders and points ledger (owners and managers can adjust points). Life spending is always computed from real orders, never stored, so a refund or cancellation can't leave it wrong.
  • Discounts, coupons and loyalty points (Operations plan). A new Promotions tab on the Data page manages discount presets (percent or fixed), coupons (code, minimum spend, use limit, dates) and the points rules. At the till the cashier taps a preset, types a coupon or redeems points; the server re-prices every one of them and never trusts an amount sent by the tablet. A coupon's last use can't be spent twice by two tills, points are earned once when a bill is paid, and refunds and cancellations take points back proportionally (a second partial refund reverses only what the first didn't). Amounts are literal in the store's own currency.
  • Split a bill by items, or pay one bill several ways. By items, each bill becomes its own order and the discount is shared out to the cent. Or one bill can be paid with several methods — an equal split between 2–10 people, or any mix such as cash plus card — recorded as one order with a Split payment method and one line per payment. The cash drawer counts only the cash part, method breakdowns list the real methods, refunds ask which payment gives the money back, and receipts print one line per payment. Orders from before this release are read exactly as they were.
  • Merge Bill. Combine saved bills into one. The merged-away bills are cancelled, never deleted, and drop out of the reports.
  • Custom Item. Ring up something that isn't on the menu — a description, a price, and a printer area (Kitchen, Bar or none, which skips the kitchen screen).
  • A send-receipt screen after every sale. Instead of a small print prompt: the amount paid, the change in large type, an email box and a WhatsApp box to send the receipt (email receipts are new), a Print Receipt button, and New Sale. A receipt can be sent at most 10 times per order.
  • Cash quick-buttons. The exact amount plus the round-ups a customer hands over — 50.000 / 100.000 in rupiah, 5 / 10 / 20 / 50 in euro and dollar, and sensible steps for any other currency.
  • Grid, Columns or List, and barcode scanning. Choose how the menu is laid out (remembered per device). Products have an optional barcode field beside the SKU; a hardware scanner adds the item straight to the bill, and the search box matches barcodes too.
  • Less top bar, and Switch user. The search, filters, view switch and scan button now share the status bar's row on tablets, giving the menu roughly one more row of items. The staff badge is a Switch user button.
  • Also fixed on the way. Receipts on 58 mm paper no longer overflow: two lines that could run past the edge (an ordinary cashier name, the "powered by" footer) now wrap, so a few receipts print slightly differently. A held bill's discount reason is cleared when the discount is removed. History's department filter now finds custom items. The public receipt page shows what was tendered and the change. A table's status can no longer be changed from another store's order.
2.88.0 — 19 septembre 2026Fonctionnalité
  • POS and Online ordering tabs. The queue opens on two tabs, each showing how many orders are open in it. The Online count turns red while anything is waiting there, so a cashier working the POS tab can see an online order arrive. GoFood, GrabFood and other channels count as Online. This replaces the hidden "Source" filter; a queue saved with the old "All" setting opens on POS.
  • Queue numbers. Every order started at the till, saved as a held bill, or placed on the storefront now gets a number — #1, #2, #3 — that restarts each morning in the store's own timezone. The bills of a split checkout share one number. Orders from before this release, and GoFood/GrabFood email imports (which carry the platform's own code), show a dash instead. You can search the queue by number ("12" or "#12"), and the number appears in Order History too, both in the table and in an order's detail.
  • Split view, now the default. Three columns: a status list with live counts, the orders list — order number, time, queue number, customer, table and total — and the details of the order you tap: who and where, the items, how it was paid, the charges and total, with the same Start / Mark as Paid / Resume / Cancel actions as the cards. On a phone or portrait tablet there is no room for three columns, so the status list becomes a row of chips and the details open as a sheet from the bottom. Grid, Compact and Board are still one tap away in the view switcher.
  • Order History gains Queue and Table columns.
2.87.0 — 19 septembre 2026Fonctionnalité
  • Invite a staff member to sign in with their own account. Open a team member in Staff Management, make sure they have an email, and choose "Send sign-in invite". They get a single-use link, valid for 7 days, to create their Epidom account — or, if that address already has one, to sign in with it and link it. Once claimed, the staff card shows a green "Verified account" label: it means the address was proven by actually opening the link, not merely that an email was sent. A link that hasn't been used yet shows "Sign-in invite sent"; re-sending replaces the old link, and changing the person's email or deactivating them cancels it.
  • Only the invited address can claim a link. The emailed link can create an account for its exact address, but it can never attach an account that already exists unless you are signed in as that account. Someone signed in to a different account who opens the link is refused, and the attempt is recorded in the audit trail as a critical action. Each link works once, and everything that can change between sending and claiming — the person being deactivated, their email being edited, an account appearing in the meantime — is checked again at the moment of claiming.
  • They land in their store's POS, and their PIN still applies. After signing in, a staff account sees its store on the Stores page (tagged "Staff", without the owner's edit and delete actions) and opens straight into the POS pages their role allows. They still pick their name and enter their PIN, exactly as on a shared tablet, and they can only ever act as themselves. Their store runs on the owner's plan; they never see billing, and no plan is created for them. Staff accounts are POS-only for now: Back Office and every owner-only page stay closed to them, so Back-Office-only roles (Admin, Finance) can't be invited yet and the invite button says why.
  • A linked staff account can do what a PIN user on the owner's tablet could — reading the store's currency and tax settings, cycling a table's status, managing reservations — but not more. Access is default-deny: anything not explicitly allowed for staff accounts stays owner-only, and editing tables, refunds and other manager actions still need a manager PIN. Cash-in/cash-out entries are not yet available to staff accounts.
  • Contact details and Contract are optional and collapse when empty. A new team member starts with both cards closed; open one to fill it in. An existing member's card opens only if it already has something in it. "Pay rate" is now "Contract": pick Freelance, Part-time, Full-time or Contract, and set pay per hour, a monthly salary, or a percentage of sales (capped at 100%).
2.86.0 — 19 septembre 2026Fonctionnalité
  • You can now transfer a store to someone else, verified by email. Profile has a new "Transfer store ownership" card: enter the new owner's email, confirm by typing the store's name, and they get a link that's valid for 7 days. Nothing changes until they accept it while signed in to a verified account on that exact address — if they don't have an account yet they create one first, and the link survives sign-up and email verification. Accepting moves the store to their account in a single transaction and the previous owner loses all access to it: their Owner staff row is deactivated and stripped of contact details, and every live staff PIN session on the store is ended. The store keeps behaving the same for its new owner — a store that was inheriting currency, tax and payment methods from the old business gets them copied onto itself first, and a business created for the recipient inherits the store's timezone and language. Billing stays with each account (the store runs on the new owner's plan; the transfer itself charges and refunds nothing), and storefront card payments follow the new owner's Stripe Connect account, or fail closed until they connect one. Only the real owner can start, view or cancel a transfer — not a Manager or any staff account, even on the owner's own signed-in device. The link is a bearer secret, so it is single-use, only ever travels in request bodies (never a URL the audit trail or platform logs would record), and each step is recorded as a CRITICAL audit action.
  • Switching from the Owner to a Cashier no longer leaves the Owner's page on screen. Picking a staff member on the "Who's using this device?" screen only updated the client, so a Back Office page already rendered for the Owner (Staff Management, say) stayed visible under the Cashier's name. It now reloads so the server-side access checks run for the new person and send a Cashier to POS.
  • "Switch Account" is now one thing everywhere, and no longer costs you your PIN. The separate "back to Owner (PIN)" shortcut is gone from both the Back Office menu and POS's "More" menu. Switch Account shows the picker without ending your session, so backing out returns you to exactly where you were — labelled "POS" or "Back Office" for the screen you came from — with no PIN re-entry. Only picking someone else, or "Log Out of Staff Session", changes who is signed in.
  • The Owner row in Staff Management is now locked down. Its page access reads MASTER — every page, all checked and not editable — and its Role, Contact Details, Pay Rate and PIN fields are removed or explained (the PIN that matters is the Owner PIN in Profile), with a pointer to the transfer card.
  • Sign-in and sign-up honour a `?next=` deep link (validated as a same-site path), so an invited new owner lands back on the transfer page after verifying their email; Google sign-in now respects it too.
2.85.0 — 17 septembre 2026Fonctionnalité
  • `/owner` (the multi-outlet Enterprise rollup) moved into the dashboard shell. It previously lived entirely outside it — no nav entry, no shell chrome, unreachable by any staff persona, discoverable only via one button buried in Finance — despite nav.owner's translation key already existing, unused, in all three locales. Now gated and reachable exactly like Finance, with the old bare /owner redirecting so existing links still resolve. Each row in its per-store table now links to that store's own Finance page, closing a drill-down gap the roadmap had named as a Phase 5 acceptance criterion but never shipped.
  • `/menu` and Storefront's Menu tab were duplicate entry points into the identical editor, inconsistently gated — /menu required the POS plan, Storefront's tab (a FREE-tier user's actual only path to publish a menu) had no gate at all. Consolidated into Storefront's Menu tab (?tab=menu URL sync); /menu is now a redirect. A staffer previously grantable only the narrower /menu permission keeps that exact access — Storefront now detects a menu-only session and renders just the editor, no other tabs.
  • Nav rail cleanup left over from Phase 1's POS Mode split: the "Point of Sale" section had been reduced to a single orphaned item after that split moved everything else out. Regrouped by job — General, Operations, Reports (Finance + Owner), Account (Profile, Billing, Custom Development) — instead of by leftover plan-tier labels.
  • Locked nav items now explain the business event that earns the tier, not a generic "Upgrade to X" — matching this project's own written upsell philosophy (STRATEGY.md), and rendered as a visible line rather than a hover tooltip that was invisible on the mobile drawer.
2.84.1 — 16 septembre 2026Correctif
  • Materials/Products pagination could still get stuck showing only the first page. The server-rendered initial list only ever fetches one page (50 materials / 20 products), and the client seeded its total from that page's own length instead of the real store-wide count — so on every fresh page load, pagination computed totalPages: 1 and simply didn't render for roughly the first 20-30 seconds, until a background refetch quietly corrected it. Explains "sometimes it works, sometimes we're blocked at 50" exactly, and the identical bug existed in Products too (latent — no store had tested it with >50 products). The server component's real count is now threaded through instead of being thrown away. Verified: calling the actual server fetch function directly against this store's data now returns total: 94, correctly computing 2 pages.
  • Cancelling or refunding an order didn't visibly change the Finance page — or the dashboard's own analytics tiles. The order-status and refund mutations never invalidated any revenue-reading query. Checked against the live database first: the underlying numbers were always correct (all NON_REVENUE_STATUSES-filtered routes exclude cancelled orders correctly); only the screen wasn't told to refresh, and it wasn't just the Finance page's dozen finance-* queries — the dashboard's own analytics tiles use an entirely separate analytics-* key namespace (found by cross-referencing every revenue-filtering API route against its actual consumer, not by re-reading the ticket), plus the Enterprise multi-store rollup (owner-summary) and the Storefront editor's analytics tab (storefront-analytics*). All four are now invalidated together, and forced to refetch even if the tab showing them isn't the one currently open.
  • Live Operations card — confirmed correct, and now proven correct end-to-end. The absence-exclusion fix from 2.84.0 is intact; this store had never published a schedule or recorded a single attendance/absence event, so the card could only show its all-zero empty state regardless of the fix. Reproduced the exact scenario against real data: published a shift, confirmed the staff member appeared in the late list (783 minutes late), reported an absence for them, confirmed they were correctly excluded — then cleaned up the test rows. See STATUS.md for the same steps if you want to see it in the UI yourself.
2.84.0 — 15 septembre 2026Correctif
  • You can now see every raw material you have, not just the first 50. The Data > Materials list quietly capped itself at 50 items with no way to reach the rest — with 94 materials entered, 44 were simply invisible. It now paginates the same way Products already does (10/20/50/100 per page, with page controls), and the Management > Edit Stock list (which showed the same capped count) now requests the API's full 100-item page instead of the 50-item default.
  • Stock can now be reset to 0 in one click. Management > Edit Stock's "Adjust Stock" only ever took a manual +/- delta, so zeroing out an item meant typing its exact current balance by hand. A new "Reset to 0" quick action posts the correct adjustment for you.
  • Cancelling an order now says, in the confirmation itself, that it stops counting toward your financial reports — it already did, but nothing told you so, and the confirmation dialog's French translation was missing entirely (silently falling back to English).
  • A staff member you marked absent for the day no longer sits in the dashboard's "not clocked in" list with a climbing late timer. The Live Operations card excluded on-duty and clocked-in staff from that list, but not staff who'd already reported an absence — so someone correctly marked absent still showed up looking later and later all day.
  • A production batch's ingredient table no longer shows raw English words on a phone. Three of its mobile-only field labels ("Qty:", "Cost:", "Total:") were hardcoded instead of translated, so they stayed in English even on a French or Indonesian device.
  • A day can now hold more than one shift for the same staff member (e.g. 8h-10h and 14h-16h as two separate blocks). The roster grid already stored multiple shifts per day correctly but only ever let you add or edit the first one — there's now an explicit "add shift" control per day, and clicking any existing shift edits that one specifically.
2.83.0 — 7 septembre 2026UX
  • The dashboard menu is in the order you actually work in. Each group now opens with the page you reach for first: your storefront sits directly under your profile, the menu comes before the till in Point of Sale, and Operations starts with your data rather than your stock. Nothing was added or removed — the same pages, in the order a day actually runs.
  • When you ask us who changed something, we can answer with a person now. Our internal activity log named whoever made each change but never said which restaurant they were working in or how to reach them, so two cashiers who share a first name at two different outlets were impossible to tell apart. Its per-person list now shows the restaurants each one worked in, their email address and their job title beside the name, and the log can be searched by restaurant.
  • Those answers are also correct now. Whenever we narrowed the log to one severity or one outcome, the "destructive" and "refused" counts sitting beside each person threw that filter away and used their own instead of narrowing inside it, so somebody with three actions on screen could be shown a destructive count drawn from a far wider set. Two people could also have their counts merged when an automated job and an external service happened to share a name.
  • The detail panel is readable again. Its text ran flush into both edges of the panel with no margin, and on a screen with the app zoomed in or out it either stopped short of the bottom or pushed its own buttons off the end. A revert preview also used to stay on screen when we moved to a different entry, which could aim an undo at the wrong action.
2.82.0 — 3 septembre 2026Fonctionnalité
  • You can now see everything that happened to your account. Your profile has a new Activity section with two lists: what you did, and what was changed on your account by someone else. Until now, if support reset your password or changed your plan, nothing anywhere told you it had happened.
  • And you get an email the moment it does. Any change support makes to your account — a password reset, a temporary password, a plan change, a reactivation — now sends you a message straight away. If it wasn't expected, there's a "This wasn't me" button next to the entry that flags it for the team to look at.
  • Nothing an administrator does goes unrecorded any more. Every action on the platform is now written down: who did it, when, from where, and what it touched. Deleting an account, resetting someone's data, changing a plan or granting admin rights all raise an alert to the team as they happen.
  • Most mistakes can now be undone. Where an action can be safely reversed, it can be reversed from the log with one click — after a preview that says exactly what will change. Where it cannot, the log says so and explains why instead of pretending. Deleting an account or wiping a store now takes a full copy of the data first, so it can be put back.
  • The log refuses to do anything dangerous. It will not undo a change if someone else has since edited the same thing, if the order sits inside a till session that has already been counted and signed off, or if reversing it would leave your stock figures disagreeing with your stock history. In each case it tells you which of those it is.
  • Your feedback tickets refresh by themselves. The admin feedback list used to only update for whoever clicked; a status changed anywhere else stayed invisible until a reload.
  • Activity is kept for 12 months and then permanently deleted, matching what the Terms already promise. Copies taken before a deletion are held for 90 days.
2.81.0 — 3 septembre 2026Fonctionnalité
  • The daily report now tells you how much cash should be in the register. Until now it printed what you sold and, if you'd picked a specific till session, the float you started with — but never the one number you actually need at closing time. Open the daily report and there's now a Cash on Hand block: opening float, cash sales, refunds paid back out, tips, cash in, paid-outs, safe drops and tips handed to staff, then the total that should be sitting in the drawer.
  • It works while you're still open. The expected-cash figure used to be written only at the moment a cashier closed their till, so mid-day it simply didn't exist. It's now calculated live, and a report printed mid-shift is labelled provisional so nobody mistakes it for a signed-off end-of-day count.
  • A day with two cashiers finally adds up. Running the report over a date range instead of one till session now gives a store-wide figure covering every till that was open, plus cash taken at the counter that wasn't tied to any of them.
  • New Cash In / Out button at the till. Record a tip, top up the float, pay a supplier in cash, or drop takings into the safe — each one lands in the drawer maths straight away and shows up on the staff log with its reason. Anything that takes money out requires a reason, because "the till is short" and "we paid the vegetable man" should never look the same.
  • Three counting mistakes fixed. An order marked delivered but not yet paid was being counted as cash in the drawer, so tills looked short whenever a customer hadn't settled up. Refunds were never subtracted at all. And a refund issued the morning after a sale was silently rewriting the previous day's takings — refunds now count against the day the money actually left the drawer.
  • The Finance cash page and the dashboard show the same numbers. The cash reconciliation table gained the full per-category breakdown and a totals row, and the dashboard's open-till card now shows expected cash alongside the float it started with. Every screen reads from one shared calculation, so no two of them can disagree.
2.80.0 — 3 septembre 2026Fonctionnalité
  • Customers can now enter their own WhatsApp number on the customer screen. A single button on their side of the till opens a panel explaining that if they leave a number, their receipt is sent to that WhatsApp once they've paid. It's the only thing on that screen they can touch — everything else stays read-only.
  • The number types itself into the cashier's checkout. Whatever the customer enters lands in the phone field of the payment dialog, so nobody has to read it out across the counter or key it in twice. It only ever fills an empty field — if the cashier already typed a number, the customer's entry never overwrites it, and the cashier still reviews it and still confirms the order.
  • No keyboard needed. The number is entered on a large on-screen keypad, because a customer-facing screen usually has no keyboard, and on a touch monitor the pop-up keyboard covers the field you're filling. The country code sits next to it with the flag, defaulting to your store's own country, and all 245 countries are there behind a searchable list.
  • The number is checked before it goes anywhere. The confirm button only lights up once the number is actually valid for the chosen country, so a half-typed number can't reach the cashier's form. Customers can remove a number they entered by mistake, and it's cleared automatically once the order is placed so the next customer never inherits it.
2.79.0 — 2 septembre 2026UX
  • Every page now loads about a third less code. The app was sending all three languages — English, French and Indonesian — to every device on every page, roughly 550KB of text to display one language. Each language is now its own download, and you only get the one you read. On the dashboard that is 118KB less over the connection and about 370KB less for the browser to work through before anything appears, which is felt most on the Android tablets behind the counter. Nothing was dropped: every phrase in all three languages is still there.
  • The low-stock check on every dashboard page got much lighter. Opening any dashboard page loads the alert badge in the sidebar, and that was reading every column of every raw material you have, plus every column of every supplier attached to them, to use nine values. It now asks only for what it shows. The alerts themselves are unchanged — including the warning for materials that have gone below zero.
  • Pages stop asking the database the same question twice. Checking that a store is yours ran two lookups one after the other, and repeated them for the layout, the page and again underneath. Those two now run together and the answer is reused for the rest of the request. The staff-session check had the same problem and got the same fix.
  • The Profile page fetches its two pieces of data at the same time instead of waiting for the first before starting the second.
  • Prices and offline status no longer re-render the whole screen. Two pieces of shared state that wrap the entire signed-in app were rebuilding themselves on every render, which made every component reading them redraw too — several times per page load. They now only change when something in them actually changes.
2.78.0 — 2 septembre 2026Fonctionnalité
  • The till can now show the order on a second screen facing the customer. Turn it on from the new screen icon in the Cashier header, open its window, and drag it onto your customer-facing monitor. It mirrors the register live: the item you just rang up in large type on the left over your store's logo, the full running receipt on the right, and the total across the bottom. Nothing on it is tappable — it's a display, not a second register.
  • It's off by default and set per till. The toggle sits next to the printer settings and works the same way: each device remembers its own answer, so a counter with a customer screen can have it on while the tablet by the kitchen doesn't. Switching it off puts an open display window into standby instead of leaving a stale order frozen on it.
  • The display wears your storefront's branding. It takes your logo, display name and theme colour straight from your storefront, and picks light or dark text so a pale brand colour stays readable. A store with no storefront yet falls back to its own name and the Epidom mark.
  • A settled order says thank you. When you confirm a payment the customer's screen shows the confirmation, the amount and the order number for a few seconds, then clears itself ready for the next customer. It works offline too — an order queued while the connection is down gets the same confirmation.
2.77.0 — 16 août 2026Correctif
  • A new order now appears in Orders to Place straight away. Creating an order saved it correctly — with its supplier, quantities and expiration date — but the list on the Stock page kept showing "No Orders to Place", so it looked like nothing had happened. Two separate faults were hiding it: the page loaded only orders already sent to the supplier, skipping the ones still waiting to be placed, and every refresh after that read the reply from the server in the wrong shape and came back with nothing at all. Both fixed, and the list now also stays correct after you mark an order as placed.
  • "Send to Supplier" works again. It reads a single order the same broken way, so the dialog opened with nothing loaded and its Send button permanently greyed out.
  • Errors say what went wrong. Failing to create, update or cancel an order showed "[object Object]" instead of the reason, and a store on the POS plan got a blank "Failed to fetch supplier orders" instead of the message explaining that supplier management needs the Operations plan.
  • Stock movements show up again. The same underlying fault was hiding your movement history in three places: the Movements tab under Stock, the Adjustment History on each item, and the "Recent Movements" card on your Dashboard. All three were reading the server's reply the wrong way and quietly showing nothing, however much history you had.
  • Importing stock reports the right numbers. The "imported / failed" count after a CSV import was read from the wrong place, so the summary came back blank.
  • Finishing or cancelling a production batch returns the real batch. Both were reading the reply the wrong way, and the count shown after deleting several products or recipes at once had the same fault.
  • Hovering a tab no longer empties a list. Preloading suppliers, products or recipes (which happens when you hover a tab, to make it open instantly) stored them in the wrong shape, so the list it was meant to speed up could come up empty until the cached copy expired — up to ten minutes for the supplier picker. The clearest symptom: hover the Products tab, then open the record-waste dialog, and its item picker had nothing in it.
  • The Owner dashboard loads again. It was asking the server for the wrong address, so the whole page failed with nothing to show.
  • Failed image uploads say why. Rejecting a photo — too large, wrong type — showed a generic "Upload failed" instead of the actual reason, both on the shared image picker and when saving a store logo.
  • Exports on a locked plan explain themselves. Exporting suppliers, products or recipes without the plan for it showed a generic failure instead of telling you the export needs an upgrade.
2.76.0 — 16 août 2026Fonctionnalité
  • A custom price now says which plan it buys. When we agree a special price with you, it's quoted against a specific plan — POS, Operations or Enterprise — instead of just being a number attached to whatever you were on.
  • Paying an agreed price is one button. The quote appears at the top of your Billing page with the plan and the amount, and a "Pay with Stripe" button that charges exactly that price on the cycle we agreed. Your plan switches on as soon as Stripe confirms the payment.
  • While a quote is waiting to be paid, the plan features pause. Your old subscription is closed the moment the new price is set, so you're not paying the previous rate for it, and any locked page sends you to Billing to settle the new one. Billing, your storefront and your account settings stay open the whole time, and nothing in your data is touched.
2.75.0 — 16 août 2026Fonctionnalité
  • The supplier list on a new order is no longer empty. Creating an order from the Stock page's "Create Order" button showed a supplier picker with nothing in it, so the order could never be submitted. It now lists every supplier you have on file — the ones who already carry that material first, with their agreed price and your preferred one at the top, and the rest below in case you're ordering from someone else this once. Their phone and email appear under the picker so you can call before you confirm.
  • Orders can now carry a requested expiration date (DLC). Ask the supplier for a use-by date, and it prints on the order and follows the goods: when you mark the delivery received, the date lands on the raw material for you, so it's typed once instead of twice. Leave it empty for flour, sugar and anything else that doesn't need one. It's on both the single-item order and the bulk order.
  • Print an order quote. Every order gets a "Print order quote" button — right after you create it, and again from the Orders to Place list. The document carries your store's name, address, phone and email, the supplier's contacts, every line with quantity, DLC, unit price and total, the order status, and a signature strip for both sides. Print it or save it as PDF.
  • Creating an order works again. The delivery date the form sends was being rejected before it ever reached the database, so "Create Order" failed every time. Fixed for both the single and bulk order forms.
  • The printed quote's totals are labelled properly. Subtotal, tax, shipping and total were printing as raw internal names.
2.74.0 — 16 août 2026Correctif
  • A batch is now baked whole. If your recipe makes 5 baguettes and you ask the kitchen for 3, it takes the ingredients for one whole batch and puts 5 on the shelf. Before, it took three fifths of the flour — which is not something you can do with a dough. Nothing changes for dishes cooked to order: those still use exactly what the portions need.
  • Recipes now say how they're made. Every recipe is either cooked when it's ordered — which is all of yours today, and unchanged — or made ahead in batches. Only batch recipes show up on the Production page, so a dish that's cooked fresh can't be started as a batch by mistake and have its ingredients taken twice.
  • Duplicating a recipe keeps its settings. A copy used to come back as a Kitchen recipe cooked to order, whatever the original was.
2.73.0 — 16 août 2026Correctif
  • Your recipes now work the way you actually cook. Everything with a recipe is treated as made fresh when it's ordered, so the raw ingredients come out of stock on every sale — rice, chicken, eggs, spices. Items you buy in ready to sell keep being counted on a shelf, and you manage those from the Stock page for adjustments and reordering.
  • Each product now says how it's made, right on the card. "Cooked to order", "Counted on a shelf" or "Not tracked" — so you can see at a glance which is which. Change it in Edit, under "How do you make this?", if you prepare something ahead in batches.
  • The Production screen now shows how it works. A small bakery diagram walks through it: flour and butter go in when you bake, twenty loaves sit in stock, and each sale takes one off the shelf — so it's clear the ingredients come out once, not twice.
  • The changelog reads properly now. Bold text, commands and links were showing as raw symbols, and long lines ran off the side of the screen on a phone. Fixed on both this page and the public one.
2.72.0 — 16 août 2026Infra
  • Retired two leftover fields from the old stock model. Nothing you can see changes — the "track stock" switch on custom items works exactly as before. Under the hood it now reads from the single field that decides how each product is made, instead of a second copy that could drift out of step with it.
  • Added a self-check you can run against a real database. It places orders, watches the stock come out, checks the finance report picks it up, logs a prep run and cancels an order — then confirms every number lines up. Run pnpm tsx --env-file=.env scripts/verify-stock-flow.ts.
2.71.0 — 16 août 2026Fonctionnalité
  • Today's prep, with one tap to log it. The Production tab now opens with a list of what to make: everything you count that has dropped below the level you set, with the number already filled in. Tap "Made it" and the ingredients come out and the stock goes up in one go — no more four-field form, which is why prep went unlogged in the first place.
  • We won't take the same ingredients twice. If something sold before you got round to making it, its ingredients already came out at the till. Log that batch afterwards and we only take what's left to take, and only add to your shelf what's actually still on it. The prep list tells you when this applies instead of quietly doing different sums.
  • End-of-day count sheet. Enter what's really on the shelf and we record the difference. This is what finally puts the cost of food you made but binned into your figures — until now it simply vanished from the books.
  • Offline orders can no longer be sent twice. If the connection dropped at the wrong moment while an offline order was syncing, you could end up with the same order — and the same stock coming out — recorded twice. Each queued order now carries its own tag and the second attempt is recognised instead of duplicated.
  • Recipe cost changes now reach the product. When an ingredient price changes, the products made from that recipe update their cost too, so your profit figures stay honest. Tick "Customize manually" on any product to keep a price you typed yourself.
  • The offline sync message now speaks your language. It was hardcoded in Indonesian.
2.70.1 — 16 août 2026Correctif
  • Zoom now fills the screen instead of shrinking away from it. Zooming out left the page stranded in a box that stopped short of the window — about three quarters of the width at 70% — with dead space down the right and along the bottom. Every part of the layout that was sized against the screen now takes the zoom into account, so the page occupies the whole window at every level and zooming out genuinely fits more on screen. Nothing changes at 100%.
  • Dialogs stay reachable when zoomed in. At 125% and 150% pop-up windows were being drawn taller than the screen, pushing their buttons off the bottom with no way to scroll to them. They now size to what you can actually see.
  • Menus now open where you clicked, at any zoom. The account menu and every other drop-down was drifting away from the button that opened it once you zoomed — far enough at 125% and 150% to slide off the right of the screen and be cut in half. They now sit against their button at every level, and scale with the rest of the page.
  • The menu bar and the page below it stay lined up at every zoom level.
2.70.0 — 16 août 2026Correctif
  • Ingredients now actually come out of stock when you sell something. They were not. A dish could be sold all day without a gram of flour leaving your inventory, because of a broken link between a product and its recipe. Every product now says plainly how it is made — you count how many you have, you make it fresh when it's ordered, or you don't track it — and stock comes out to match.
  • Your cost and profit figures will change, and the new ones are the real ones. Because ingredients were never leaving stock, cost of goods was reading close to zero for most shops and gross profit was reading far too high. Now that ingredients are counted properly, expect your margin to drop to its true level. Past months are left exactly as they were reported, so only new sales reflect the corrected figures.
  • Selling more than you prepared no longer loses the ingredients. If you sell ten croissants when you counted six, the last four are treated as made to order and their ingredients come out then. If you log that batch afterwards, we know it was already accounted for and will not take the ingredients twice.
  • Products can now show as "oversold". Selling past zero used to quietly show as healthy stock. It now shows the real, below-zero number so you can count what is actually there and correct it.
  • Extras and add-ons now count towards your costs. The ingredients used by a modifier were already coming out of stock, but their cost was missing from your profit figures.
  • Delivery-app orders are marked as uncosted rather than counted as pure profit. Orders coming in from delivery apps are not linked to your products, so their cost cannot be known. The finance page now says how many there were instead of treating them as 100% margin.
2.69.2 — 14 août 2026UX
  • The mobile menu is now on the right. The menu button has moved to the right end of the top bar and the menu itself slides in from the right, so it falls under your thumb instead of asking you to stretch across the screen. Swiping in from the right edge opens it, and swiping back to the right closes it.
  • Opening the mobile menu no longer built it twice. A second, invisible copy of the menu was opening at the same time as the real one, which is why the background behind it looked darker than it should. Fixed.
2.69.1 — 14 août 2026UX
  • One button in the toolbar instead of two. Install and Offline & Sync were both showing at once, which read as the same button twice. They are now two states of one slot: you get Install while there is something to install, and it becomes Offline & Sync once the app is installed. Neither one flashes on screen before the app has worked out which it should be.
2.69.0 — 14 août 2026Correctif
  • The installed app now opens when you launch it with no connection. Tapping the Epidom icon with the wifi down used to land on the "you're offline" card every single time, because the app asked our server which store to open before it could show you anything. It now works that out on the device and takes you straight back to the screen you were last on.
  • Offline & Sync no longer vanishes once you install the app. Installing Epidom used to hide the whole panel — on the one device where working without a connection matters most, there was no way to check whether anything was actually saved. It stays now, with the Offline Mode switch visible and locked on.
  • You can see exactly what will work offline. Offline & Sync now lists every page that opens without a connection (POS Cashier, Order Queue, Kitchen & Bar, Data, Staff, Schedule, Dashboard) and every kind of data saved on the device — menu and prices, live orders, cashier list, materials and stock, staff and schedules — each marked Ready or Not saved, with how many records and when they were last refreshed. No more guessing.
  • Pages are now saved for offline, not just data. Epidom used to save your figures but not the screens that display them, so a page you hadn't opened since launching the app simply couldn't load without a connection. Turning on Offline Mode, and every "Sync now", now saves the screens themselves too — there's also a "Save pages for offline" button to do it on demand.
  • Testing offline on a development server now says so. Offline storage is deliberately switched off on localhost so a development server can never hand you stale files. The panel now explains that outright instead of leaving you to conclude offline mode is broken.
2.68.0 — 13 août 2026UX
  • The Install button works again. The install window was being hidden by the app itself, so pressing Install did nothing at all on any device. Fixed.
  • Installing the app is now one tap. The Install button used to open an Epidom window that mostly existed to hold a second Install button. It now opens the real installer straight away — a one-tap install on Android and desktop Chrome, and clear step-by-step instructions with pictures on iPhone and iPad, where Apple gives no other way in. It also opens reliably now even if you dismissed it once before.
  • The install window and the home-screen entry are in English. The app name, description and the two shortcuts ("Cashier", "Order Queue") were Indonesian-only.
  • Offline & Sync moved to its own button, next to Install. The Offline Mode switch, the last-synced time, "Sync now" and the storage readout all still live in one place — they're just no longer buried inside the install window. "Sync now" is also a proper size to hit on a tablet; it was previously a 24-pixel target.
  • Adding Epidom to an iPad or iPhone home screen now looks like an app. It picks up the real Epidom icon instead of a blurry snapshot of the page, opens without Safari's address bar, and shows a dark status bar that matches the app instead of a white strip.
  • Marketing and public pages were sending browsers a conflicting, outdated set of app settings — a different theme colour and a light status bar — which overrode the correct ones. Every page now sends one consistent set.
  • Long-pressing the installed icon shows the Epidom mark next to "Kasir" and "Antrian Pesanan" instead of a blank placeholder.
  • The home-screen icon is no longer a half-megabyte download. The manifest was offering a 580 KB icon file that some devices preferred over the 6 KB one meant for the job.
  • Order and stock notifications now use the app icon rather than a full-colour logo that was ten times the size and didn't fit the notification badge shape.
  • Offline & Sync is fully translated into French. Eleven labels in that panel had no French text and were printing their internal names on screen.
2.67.1 — 11 août 2026Correctif
  • Stock figures now update everywhere the moment stock moves. Running a recipe in Production correctly took the ingredients out of inventory, but Management and Data kept showing the old figure for up to half a minute, which read as "the deduction never happened". Recording waste and receiving a supplier delivery had the same lag. Every page showing that material now updates as soon as the change lands, on every open tab and device.
  • Low-stock warnings now come from production and waste too, not only sales. A recipe run or a spoilage entry could take an ingredient below its minimum in complete silence — the alert only ever fired when something was sold. All three now raise the same warning and send the same notification.
  • The checkout window now tells you when ingredients come out of stock. With the Kitchen & Bar display on, ringing up an order doesn't move stock yet — it comes out when the order is marked delivered. With it off, stock moves as soon as payment is confirmed. The window now says which applies to your store, instead of leaving it to be guessed.
  • Menu items that aren't linked to a product are now marked. Selling one moves no stock at all, which is a perfectly valid setup but was invisible. They now carry a "No stock link" tag explaining it, alongside the existing "From Product" tag.
  • French menu labels are readable again. Two labels in the menu editor had no French translation and printed their internal name (storefront.menu.fromProductBadge) on screen instead of a word.
2.67.0 — 11 août 2026Fonctionnalité
  • Filter your order history to one cashier shift, from open to close. Add the new Shift filter in Order History and pick a till session — the table narrows to everything sold between that session's open and close, and the payment-method totals recompute with it. It's a time window, not just orders rung on that till, so online and delivery orders taken while the drawer was open are counted too. A session that's still open runs up to right now and is labelled as such, so a mid-shift look doesn't read as a final total.
  • Print a proper daily report, on receipt paper or on screen. The Export button in Order History now offers a daily report alongside the existing PDF order list. It lays out the same blocks a till report is expected to have: sales, discounts, service charge and tax down to the total; invoice count and average per invoice; a cancellation summary; splits by sale type, by guest, and by payment method; and a per-product breakdown grouped under each menu category with its own subtotal. Scope it to a shift and it also reconciles the cash drawer — opening float, expected, counted, and the difference. Print it straight to a Bluetooth thermal printer at 58mm or 80mm, or open it as a page and print from any printer, including on iPad where Bluetooth printing isn't available.
  • Every daily report has its own link. The report opens at a stable address you can bookmark, re-open, and re-print later without setting the filters up again — the same way a customer receipt already works.
  • Count guests at checkout, and see what that's worth. Dine-in checkout now has a guest-count stepper, carried through if you park the order and finalise it later. The daily report turns it into total guests, average guests per day and average sales per guest. Orders taken before this shipped, and takeaway orders, simply have no guest count — they're left out of that block rather than counted as one guest each, and if nobody records guests the block doesn't print at all.
  • Offer the report the moment a shift closes. The Close Shift dialog has a "print report on close" switch — turn it on and the shift's report opens as soon as you close the drawer. It's remembered for next time. The report stays one click away either way, including mid-shift, so you can check the numbers before counting the cash.
  • Slice the whole Finance page by a till session. The same shift picker sits on Finance. Choose a session and every figure on the page — the KPI cards, the P&L, and each tab — narrows to that window, so it reconciles exactly against the printed daily report.
2.66.0 — 11 août 2026Fonctionnalité
  • Your dashboard now shows the shop floor, and only the cards you can actually use. A new Live Operations card sits at the top: who is clocked in right now and for how long, which till sessions are open with their float and order count, and anyone who was on today's roster but hasn't clocked in yet — refreshed on its own, so you can leave the dashboard open on the counter screen. Times are counted on your shop's own clock, not the browser's, so a night shift lands on the right day.
  • The Production History chart hides itself when you don't use production. Recipe-to-batch production is off by default, and the chart was still drawing a flat zero line for everyone who never turned it on. It now appears only once production is enabled, and the row it sits in re-flows so the remaining cards fill the space instead of leaving a gap.
  • Cards you can't open no longer take up the dashboard. Stock levels, low-stock alerts, suppliers, stock movements and the new operations card belong to the Operations plan; below that tier the dashboard used to show them all locked or empty. They're now replaced by a single upgrade card, and the underlying data is no longer loaded for accounts that can't see it — so the page opens faster too.
  • Stock and alert tables read properly on a phone. The material name column was pinned to a fifth of the row, which left roughly sixty pixels for the name on a 375px screen. Name now takes the space it needs and the number columns hold a fixed width.
  • Live Operations is visible to the owner and manager accounts only — the same rule the attendance log already follows, so a cashier or kitchen login doesn't see the whole floor's clock records.
2.65.0 — 11 août 2026Fonctionnalité
  • Zoom the app in and out from your account menu. Open the menu under your name and you'll find a zoom stepper — 70%, 80%, 90%, all the way up to 150%. Zoom out to fit more of the order queue on a small counter screen, or zoom in when the kitchen display is read from across the room. It replaces reaching for the browser's own zoom, which isn't available at all on a tablet (the app locks pinch-zoom so the layout can't be dragged out of shape) or in the installed app, where there's no browser toolbar to open. The setting belongs to the device, not the account, so a shared cashier tablet keeps its zoom for whoever signs in next, and it's remembered the next time the app opens.
2.64.3 — 11 août 2026Correctif
  • Update notes are listed newest-first again. On a day that shipped several releases they came out in a jumbled order, so the latest update could sit halfway down the list instead of at the top. Release dates carry no time of day, which left same-day releases with nothing to sort them by — the version number now settles it. Affects both the What's new page in your dashboard and the public changelog.
2.64.2 — 11 août 2026Correctif
  • Orders made up entirely of custom items can be completed again. A custom product line such as "Men's Haircut" has no kitchen prep step, so it never appears on the kitchen screen — and on an order containing nothing else, Mark All Complete quietly did nothing and the order sat at In Production with no way to move it forward. It now advances the order directly. Orders that mix custom and regular items work as they did before.
  • A brief database hiccup no longer looks like being signed out. If the connection to the database dropped for a moment — routine on serverless Postgres, which closes idle connections and cold-starts — the app could not look up your session and reported "Unauthorized", which mid-checkout reads as a surprise logout rather than something to retry. The lookup now retries for a moment on its own, and if it still cannot be resolved you get a "please try again" message instead of a false logout. Creating, holding, finalizing and editing an order all go through this.
  • Order status badges are filled instead of outlined. Confirmed and Ready shared a single style and In Production was a faint outline, so the queue's most important signal was easy to miss at a glance. Each status now carries its own solid color, matching the status tiles above the queue and the color stripe down the side of each card: Confirmed blue, In Production orange, Ready green, Held grey, Cancelled red, Delivered white. They read identically in light and dark mode, across the grid, compact and board views.
2.64.1 — 11 août 2026UX
  • The Mark as Paid window now scrolls instead of running off the screen. With every payment method your store accepts listed at once, the top of the window and its Confirm button could both sit outside the screen on a laptop or tablet, with no way to reach them.
  • Only one window opens at a time now. Choosing Mark as Paid, Refund or Cancel from an order's details used to open a second window stacked on top of the first — double-dimmed background, the order details still showing around the edges. Each one now replaces the details, and closing it takes you straight back to the order where you left off.
  • Order card buttons no longer overlap on narrow cards. When an unpaid order showed both a stage action and Mark as Paid, the two labels printed over each other. Mark as Paid now shares the top row with the cancel ✕ and the main action (Complete, Start Process…) gets a full-width row below it, with longer labels shortened rather than spilling out. All three are also bigger to tap on a tablet.
  • Deleting a material, recipe or supplier from its details window asks once, not twice. The details window raised its own confirmation, which then raised the page's confirmation for the same delete.
  • Editing or deleting a material from its details window no longer leaves the details stacked behind it, matching how products, recipes and suppliers already behaved.
  • Deleting a category from Manage Categories now replaces the category list rather than stacking on top of it.
2.64.0 — 11 août 2026Correctif
  • Fixed the intermittent 404s and "page needs reloading" errors when moving between dashboard pages. The main cause was the offline cache holding on to page data from a previous release: after a new version shipped, it kept serving the old version's data, which pointed at files that no longer existed. The cache now leaves that data alone entirely and only stores genuinely static assets.
  • The installed app's shortcuts work again. Long-pressing the app icon and choosing Cashier or Order Queue used to open a "page not found" screen, because those shortcuts pointed at an address that only exists inside a specific store. They now resolve to the right store automatically, and opening the installed app goes straight to your dashboard instead of the marketing homepage.
  • Opening the app no longer lands on a dead page after a store is removed or a page is renamed. The "continue where you left off" shortcut now checks that the saved page still exists before jumping to it, and a store that is no longer yours sends you to your store list instead of a broken dashboard.
  • `/store/{id}` opens your preferred section instead of a "page not found" screen — a trimmed or hand-shared store link now works.
  • Dashboard pages show a loading skeleton while they open, and a recoverable error screen with a Try again button if something fails, instead of a blank screen you had to reload by hand.
  • Losing connection mid-shift now shows a proper offline screen that reconnects on its own, rather than the browser's error page. The app also no longer tries to reload itself while offline, which previously left the screen stuck.
  • Fixed an upgrade dead-end that could trap the browser in an endless redirect. If your plan lapsed while your last-opened page needed a higher tier, opening the app bounced you to the pricing page, which bounced you back to that page, forever — so the one page that could restore your plan was the one page you could never reach.
  • Fixed an endless loop for accounts with no store left. Deleting your last store put the app in a permanent bounce between the store list and the setup screen; you now land on setup, where a new store can actually be created.
  • Losing wifi mid-shift keeps the screen you were on. The offline fallback now restores the actual dashboard or cashier page rather than a generic offline card, since the app's code and its local order data are both still on the device. Signing out clears it, so a shared tablet never shows one account's screen to the next.
  • Connectivity is now detected by actually reaching the server roughly once a second while offline, rather than trusting the device's network indicator — which reports "connected" behind café/hotel wifi sign-in pages and when a weak link silently recovers. Queued orders and cached data now sync the moment the connection genuinely returns, and the "last synced" time stays accurate.
2.63.0 — 11 août 2026Correctif
  • Custom Products now render as their own section in POS Cashier and on the storefront, in a visually distinct block headed by the store's name for that product line, instead of appearing as just another menu category among the food and drink headings.
  • Fixed the "Track stock" toggle not saving: the product update endpoint dropped the field, so switching stock tracking on or off in the Custom Products edit dialog silently did nothing.
  • Repaired menu categories that had drifted out of sync: products edited before the category-sync fix kept their old category on the cashier and storefront. A one-off repair (pnpm tsx scripts/repair-menu-item-categories.ts, with --dry-run to preview) re-points every product-linked menu item at its product's current category.
2.62.0 — 11 août 2026Fonctionnalité
  • Products can now be marked as not stock-tracked — a service (a haircut), or any always-available item. Turn "Track stock" off and nothing is ever deducted and it can never run out; turn it on and set a quantity, and it's deducted per order like any other product. Available on Custom Products items, where it defaults to off since services are the common case there; every existing product keeps tracking stock exactly as before.
  • Fixed custom items showing as "SOLD OUT" on the storefront: they were being created as unavailable back when custom items were unconditionally hidden from the public menu. Storefront visibility is now controlled by its own setting, so that flag no longer applies — existing items are corrected automatically.
  • Fixed a product's category change not reaching the POS Cashier or storefront: renaming or reassigning a product's category updated the product itself but left the linked menu item filed under its old heading, on both screens, indefinitely. The category now syncs across (creating the menu category if needed) and pushes live to the cashier like name/price changes already did. Applies to all products, not just custom ones.
2.61.0 — 11 août 2026Fonctionnalité
  • Order Queue can now mark an in-production order complete on its own: an order that's had "Start Processing" clicked previously had no further manual action on the Order Queue at all — the only way to move it forward was switching to Kitchen & Bar and tapping every item. It now gets the same "Mark All Complete" action Kitchen & Bar has, right on the card, for stores that don't want a cashier bouncing between screens for a simple order.
2.60.0 — 11 août 2026Correctif
  • Fixed two Custom Products dataflow gaps found in operator testing: turning off the feature's master toggle on the Data page now correctly pulls those items off the public storefront too — previously the storefront-visibility toggle in Storefront Settings was checked on its own, so a store that had opted into storefront publishing kept showing custom items there even after the master feature was switched off. Also, the "name this product line" field on the Data page's disabled/explainer view now pre-fills with whatever was last saved instead of appearing blank — turning the feature off never actually cleared the name, but the input didn't show it, making it look lost.
2.59.0 — 11 août 2026Correctif
  • Fixed notes (and modifiers) disappearing when a held order is resumed for the first time: the very first time a cart was held (before it had ever been resumed once already), the order-item's note and selected modifiers were never actually written to the database — only quantity/price/name were. Resuming that order later showed the item with no note at all. A held order that had already been resumed and held again worked correctly, since that code path (a separate branch in the same route) already saved both fields — this just brings the first-hold branch in line with it.
2.58.0 — 11 août 2026Correctif
  • Order status now updates instantly on Kitchen & Bar and the Order Queue: marking an item (or a whole station) "Ready" is pushed live everywhere else in the app, but the KDS item-status route was the one gap — it never fired that push, so both screens fell back to periodic polling (up to 10s, or 15s where live push isn't configured) to notice. Also fixed a same-device gap where the KDS card kept showing "Waiting other department" for a few seconds after that station had already finished, because the local optimistic update only flipped the item, not the order.
  • Order Queue status badges (status/source/payment/unpaid) now stretch to the full width of their column instead of shrinking to fit their own text, so the badge stack reads as one clean aligned block instead of a jagged one. The status filter tiles above the queue (All/Confirmed/In Production/Ready/Held) had the same problem for a different reason — the grid was hardcoded to 6 columns for 5 tiles, leaving a dead gap the width of a whole tile; now a 5-column grid.
  • Checkout confirmation is significantly faster: creating or finalizing an order now returns as soon as the order itself is committed. Stock deduction, shortfall-batch drafting, and the background order-placed notification (an outbound network call) used to all run before the response was sent — they're now deferred to run immediately after via Next's after(), off the critical path but still guaranteed to complete. The two independent lookups needed to build the order (menu item validation, finance settings) now also run concurrently instead of one after another.
2.57.0 — 11 août 2026Fonctionnalité
  • Custom Products, round two — separated the two on/off switches and made it a real POS department: the master enable toggle (which drives POS Cashier inclusion) moved back to the Data page's Custom Products tab, where it's paired with the Operations-plan pricing wall; Storefront Settings now has its own, independent toggle purely for whether those items also publish to the public storefront link. Custom-line items now show up as a genuine third department filter — labeled with the store's own name for it — right beside Kitchen/Bar in POS Cashier, the Order Queue, and Finance Reports' revenue-by-department breakdown, instead of living in a separate section or an "Unassigned" bucket.
  • POS cart: every line item can now carry a note, not just ones with modifier options — the pencil icon on a cart line no longer waits for a menu item to have option groups before it appears.
2.55.0 — 11 août 2026Correctif
  • Custom Products redesign, based on live operator testing of 2.54.0: fixed a real currency bug where editing a custom item showed the raw IDR-stored value instead of converting to the store's display currency (e.g. a €10 cost showed as "208333,33"). Removed the per-item "Show on Menu"/"Show on Cashier" switches entirely — custom items are now never shown on the public storefront and always auto-added to POS Cashier for as long as the feature is on, no manual toggling needed. The on/off toggle and naming moved out of the Data page tab into Storefront Settings' "Storefront Features" card (alongside Online Orders/Table Reservations), now gated behind an Operations-plan upgrade wall. POS Cashier gained a dedicated section for these items, structurally separate (below) the regular Kitchen/Bar item grid rather than mixed into it. The Data page tab itself is now leaner — just the product list, or a link to Storefront Settings when the feature is off — fixing an unrelated layout gap reported alongside the above.
2.54.0 — 11 août 2026Fonctionnalité
  • Optional "Custom Products" second product line: a store can now sell something entirely unrelated to its Kitchen/Bar menu — the example that prompted this, a café that also runs a small hair-salon counter — without Epidom building anything salon-specific. Off by default; the store owner names it (e.g. "Hair Salon", "Spa Services") when turning it on from a new tab on the Data page. Items in this line skip the Kitchen/Bar KDS workflow and material stock/recipe deduction entirely (there's no such thing as inventory for a haircut), and each item has two independent visibility switches — "Show on Menu" (the customer-facing Storefront) and "Show on Cashier" (the POS Cashier sell grid) — instead of the one shared toggle regular menu items use. Revenue still flows into the same integrated Finance Reports as everything else.
2.53.0 — 11 août 2026Infra
  • Local dev and PR preview deployments no longer share the production database. Vercel's DATABASE_URL/DIRECT_URL were scoped to both production and preview — every preview build's own migration step, and any local work, ran directly against live data. Added a Neon development branch (instant copy-on-write, reset from production nightly via a new GitHub Actions workflow) and re-scoped Vercel so only production deploys touch the production branch; preview/local now point at development. No user-facing behavior change — this is entirely about protecting production from development activity.
2.52.0 — 10 août 2026Fonctionnalité
  • Admin can now set a custom price for an individual account's subscription: from the Master Admin Panel's "Manage" menu, a new "Set Custom Price" action lets the operator override what a user is billed. For a real Stripe-paying customer, it actually changes what Stripe charges them starting the next billing cycle (a live price override); for an admin-granted/comped account, it's stored as a reference figure — shown on the admin panel and that user's own Billing page — for the operator's own manual invoicing arrangement. Mainly for negotiated Enterprise deals, previously handled entirely outside the app.
2.51.0 — 10 août 2026Fonctionnalité
  • Marketing site now remembers your last chosen language: picking a language from the switcher on epidom's marketing pages (/, /pricing, etc.) now sticks on every future visit to an unprefixed URL — a bookmark, the logo click, typing the bare domain — instead of only lasting for that page load. Previously the site only auto-guessed a language once from your browser settings on first visit; an explicit pick now always wins over that guess, on a real cookie so it's honored before the page even renders.
2.50.0 — 10 août 2026Correctif
  • Fixed a redirect loop in "Resume where I left off": clicking the EPIDOM logo (the way back to the marketing homepage from inside the app) now records / itself as the last-visited page before navigating, so the marketing site loads normally instead of instantly bouncing back into the app. Previously the logo linked straight to / without updating the tracked URL, so the stale in-app URL was still "last visited" and the resume redirect fired immediately — signed-in users had no way to actually see the marketing site.
2.49.0 — 10 août 2026Fonctionnalité
  • "Resume where I left off": signed-in visitors who land on the marketing homepage are now sent straight back to the last app page they had open on that device — including filters/tabs, since most of those already live in the URL — instead of seeing marketing content. New Profile setting (replaces "Default landing page") lets you turn this off. Device-local (localStorage), cleared on logout so the next person on a shared device isn't bounced into someone else's page.
  • "Back to Stores" added to the account dropdown, alongside Switch Account/Account Access — there was no way back to the store picker from inside a store without using the browser back button.
2.48.0 — 10 août 2026Fonctionnalité
  • Offline Mode is now mandatory (no opt-out) once the app is installed as a PWA — previously it auto-enabled once but respected an explicit prior "off," which meant an installed app could still end up without offline support. disableOfflineMode itself now refuses while standalone, not just the UI.
  • PWA install flow rebuilt on `@khmyznikov/pwa-install`, replacing the hand-rolled iOS-detection code — its own richer, per-platform install guide (opened from a dedicated button), and its isUnderStandaloneMode is now the source of truth for "already installed" in this component. The install button (and the whole Offline & Sync dialog) is hidden entirely once installed — nothing left to configure once Offline Mode can't be toggled anyway. The app-preview screenshot (device-appropriate — phone vs. desktop) is still shown up front in our own dialog, same as before.
2.47.0 — 10 août 2026Fonctionnalité
  • NotificationBell: upload your own custom sound instead of only the built-in Chime/Ping — pick "Custom" to select an MP3/WAV/OGG/M4A clip (validated: audio only, 3 seconds max, 1MB max), stored on-device only (no server upload). A trash icon next to "Custom" removes it and falls back to Chime.
  • Push toggle and sound options merged into one row in the NotificationBell popover — a single switch for enabling/disabling push, plus a "customize" dropdown covering why push can't be toggled right now (blocked/iOS-install-needed) and the full sound picker, instead of two separately-bordered always-expanded sections.
  • Fixed a test regression from the MagicBell change (2.43.0): the low-stock alert test suite's Prisma mock didn't define user.findUnique, which fireLowStockAlert now calls to resolve the MagicBell recipient — crashed 3 tests. Production was never affected (the real Prisma client always has .user); this was a test-mock gap only, caught during this session's pre-push verification.
2.46.0 — 10 août 2026Correctif
  • Billing page now shows "Lifetime access" instead of a literal far-future date (e.g. "May 4th, 2126") for accounts granted a lifetime period from the admin panel — mirrors the admin user table's existing lifetime detection, now shared via one isLifetimePeriod() helper instead of two separately-maintained thresholds.
2.45.0 — 10 août 2026Correctif
  • `/admin/capacity`'s Vercel card no longer shows a raw "404" as an error — a Hobby-plan team with zero billable usage gets a real costs_not_found 404 from Vercel's billing API; that's honest zero-usage data, not a broken integration, so it now renders as "$0.00, no billable usage this period" instead of an error string.
  • Backup card now tells configured-but-never-run apart from not-configured — previously always said "Set R2ACCOUNTID..." even when R2 was fully configured and just hadn't backed up yet.
  • First real database backup run — 50 tables, ~1,800 rows, ~166KB compressed, uploaded to Cloudflare R2.
2.44.0 — 10 août 2026Correctif
  • Fixed `/admin/capacity`'s "Failed to fetch capacity" error — the orders-per-day query referenced the Order Prisma model name in raw SQL instead of its actual mapped table name (orders), which every other query on the page correctly used.
  • NotificationBell gets a custom in-app sound — a synthesized chime/ping (no audio file to host) plays when a genuinely new order/reservation/onboarding item arrives while the tab is open, with a 3-way Chime/Ping/Off picker saved per device. Doesn't apply to OS-level push or MagicBell's own channels — neither the Web Notifications API nor MagicBell support a custom delivery sound, a platform limitation this app can't work around.
2.43.0 — 10 août 2026Infra
  • Merchant alerts (new order, low/critical stock) now route through MagicBell instead of separate direct WhatsApp (Fonnte) and browser-push (VAPID) calls — one unified API, with web push/mobile push/email/in-app configurable per category in the MagicBell dashboard (SMS can be turned on later by connecting Twilio there, no code change needed). Recipient is the store's owning account, identified by email. The old push/WhatsApp infrastructure is intentionally left in place (unused by this flow, not deleted) rather than bundling a second, larger cleanup into this change. Nothing else — the in-app notification bell, customer-facing WhatsApp receipts, and transactional email — changed.
2.42.0 — 10 août 2026Fonctionnalité
  • Offline Mode: the "Install app" dialog (topbar/sidebar download icon) now doubles as an Offline & Sync settings surface, and stays visible after install instead of disappearing. A new Offline Mode toggle eagerly downloads menu, live orders/KDS, materials, staff roster, and staff schedules to the device (via IndexedDB) so POS keeps working with no connection; everything else (finance, admin, marketing) stays online-only by design. Auto-enables itself the first time the app is confirmed running installed to the home screen, without waiting for anyone to find the switch — turn it back off any time to opt out for good.
  • "Last synced: <date>" status, shown in the Offline & Sync dialog and in the POS offline banner, plus a "Sync now" button that flushes any queued offline orders and refreshes the offline data mirror together. Reconnecting automatically triggers the same refresh.
  • Offline data now survives a reload/relaunch (previously it lived only in memory and was gone the moment you left the POS screen or closed the tab).
2.41.0 — 10 août 2026Infra
  • New `/admin/capacity` dashboard: database size and per-table disk usage (auto-discovered, no hardcoded table list), row-growth for the highest-traffic tables, tenant scale (stores/users/orders-per-day), and Vercel Blob storage usage — an early-warning view for approaching a platform limit before it causes an outage.
  • Platform usage cards for Vercel and Neon (billing/consumption vs. quota) — optional, degrade to "not configured" until their API credentials are added.
  • Independent, off-platform database backup: a nightly job streams every table straight from Postgres into Cloudflare R2 (gzip-compressed), with a 90-day retention and a daily freshness check that alerts if a backup hasn't succeeded in 36+ hours. Restores are a deliberate CLI script (pnpm restore:backup), never a web action, and are documented end-to-end in the new docs/BACKUP_RESTORE.md runbook including a quarterly restore-drill checklist.
2.40.0 — 10 août 2026Fonctionnalité
  • Storefront → Analytics is now real, dynamic, and date-range driven instead of a static "Coming Soon" mock. Tracks storefront page views, menu views, item views, and WhatsApp-button clicks via a new anonymous, daily-rotating visitor fingerprint (no cookies, no raw IP stored, bot/crawler and chat-app link-preview fetches excluded so sharing the link into WhatsApp doesn't inflate the numbers). The tab now shows real unique visitors with a trend vs. the prior period, menu-view and WhatsApp chat-conversion rates, storefront-attributed orders/revenue (from existing Order.source = STOREFRONT data), a visitor trend chart, and top viewed/ordered items.
2.39.0 — 9 août 2026UX
  • Kitchen & Bar / Order Queue ticket timers no longer show absurd raw minute counts on old tickets (e.g. "51482m 50s"). Past an hour, the live mm:ss counter now switches to a human duration — "8hrs 9mins ago", "7 days ago", "1 month 4 days ago" — instead of continuing to tick in raw minutes.
  • Order History table's Date column now reads "<weekday>, <day> <month>" (e.g. "Thursday, 9 July") with the time on a second line, instead of a year-inclusive timestamp that made the column harder to scan. The exact weekday, date, year, and timezone are still there in full — now on the order detail dialog, which shows a precise timestamp with both the timezone offset and full zone name (e.g. "Sunday, Jul 9, 2026, 4:00 PM GMT+7 (Western Indonesia Time)") for the order date, delivered date, and last receipt send time.
2.38.0 — 9 août 2026Fonctionnalité
  • Menu Editor: categories and items can now actually be dragged to reorder. The grip handles were previously decorative; they now use @dnd-kit (new dependency) to reorder categories among themselves, and items within a category, persisting the new displayOrder via the existing update endpoints. Touch-safe (40px drag-handle hit targets, touch-action: none) for iPad/Android use.
  • Product-linked menu items: deleting is now framed as "remove from POS menu," not "delete." The item's trash icon and confirm dialog say "Remove from POS menu" and clarify the product data is kept — only its POS listing goes away. Deleting a category that contains product-linked items shows the same clarified wording for its "delete items" option.
  • Product-linked menu items no longer show a Category or Modifiers editor in the Menu Editor. Both are owned by the Product (Product.category, Product.optionGroups) and edited there via the existing "Edit in Products" link — duplicating them in the Menu Editor risked silent drift. Description and photo remain editable in place since those aren't synced from the product.
2.37.0 — 9 août 2026Fonctionnalité
  • Kitchen & Bar display and the Order Queue are now one module. Both pages share a single "Active Queue" setting: turning Kitchen & Bar display off now also empties the Order Queue (new orders skip straight to History as Delivered), and a matching toggle was added to the Order Queue page itself — turning it off does the same and flips Kitchen & Bar display off too. Either page's toggle controls the same store-wide setting, in sync everywhere.
  • Closed a gap where a Pay Later order could still sit on Pending in the queue even with the display off — it now also goes straight to Delivered (still unpaid, still followable from Order History's Mark Paid action).
  • Hold order is now disabled while the Active Queue is off, since there's no queue left to park it in or resume it from.
2.36.0 — 9 août 2026Fonctionnalité
  • Menu Editor: move an item to a different category (or create one) without deleting and re-adding it. The Edit Item dialog now has a Category field (searchable, creatable) that reassigns the item's MenuCategory in place.
  • POS menu items linked to a Product are now clearly marked and protected from drift. A "From Product" badge + tooltip appears on any menu item backed by a Product; its Edit dialog locks Name/Price/Department (owned by the product and synced one-way) and offers an "Edit in Products" button that deep-links to that product's edit dialog on the Data page instead. Category, description, image, and modifiers stay editable in the Menu Editor.
  • Fixed the Data > Products "in POS menu" icon going stale. Adding/removing/editing a menu item (from the Menu Editor, another tab, or another device) now publishes a menu.changed realtime event that the Products page subscribes to, instead of relying only on window-focus refetch; the Menu Editor also invalidates the Products page's linked-status cache directly for instant same-tab feedback.
  • Added a bulk "Remove from Menu" action to the Products page's multi-select toolbar, mirroring the existing bulk "Add to Menu".
  • The "not yet in POS menu" icon on a product card no longer looks similar to the green "already in menu" one — it's now a neutral/muted color so the two states are easier to tell apart at a glance.
2.35.0 — 9 août 2026UX
  • Email and WhatsApp Number now format live and show their own inline error, like Username already did. Add/Edit Staff previously only caught a bad email or phone value on submit (or worse, silently on Add — those two fields had no inline error rendering at all). WhatsApp Number now strips non-digits while typing (keeping a leading +); Email strips whitespace and lowercases. Both re-validate as you type — Add via a watch + trigger effect (react-hook-form), Edit via a new shared optionalEmailSchema (common.schemas.ts, alongside the existing phoneSchema) checked directly against the plain useState fields — and show the specific reason inline instead of only a generic toast after Save.
2.34.1 — 9 août 2026Correctif
  • Staff save failures now say which field is wrong. Saving the Add/Edit Staff dialog surfaced a bare "Validation failed" toast on any Zod validation error (e.g. a non-phone value sitting in WhatsApp Number), giving no way to tell what to fix — the actual per-field reason was already being computed server-side (parsed.error.flatten()) but discarded on the way to the toast, which only ever read the response's generic top-level message. Both dialogs now surface the real field + reason (e.g. "WhatsApp Number: Invalid phone number format").
2.34.0 — 9 août 2026Correctif
  • Staff filter dropdowns (Finance Reports, POS Order History, Order Queue) no longer drop staff the moment they're deactivated. Finance Reports and Order History both queried/filtered isActive: true, so a staff member's own past orders became impossible to filter by as soon as they were deactivated — with no way to isolate their historical revenue or transactions. All three now keep deactivated staff selectable, labeled "(Inactive)", instead of quietly removing the option along with the person.
  • Hid "Switch Account" from the account dropdown when there's no one to switch to. Previously always shown to the owner regardless of roster state, it reloaded into an empty picker whenever the store had zero staff or only inactive ones. Now hidden unless at least one active, non-Owner staff account exists (mirrors the existing zero-staff bypass check in the dashboard layout).
2.33.0 — 9 août 2026UX
  • Custom staff role moved into the Role dropdown, and it now actually shows up. The Add/Edit Staff dialogs previously had an always-visible "Custom role label" text field sitting below the Role select — confusing since it looked mandatory, and its value was silently discarded by the display layer (the staff table's role Badge never read customRoleLabel, only the base role name). Replaced with a "Custom…" entry inside the Role dropdown itself: selecting it reveals a single inline text input right there, and the staff list now shows that custom title instead of the base role wherever it's set.
  • Staff username field now auto-formats while typing instead of only rejecting invalid characters on submit — lowercases and strips anything outside a-z0-9_. live, matching the existing backend usernameSchema regex, in both the Add and Edit Staff forms.
2.32.0 — 8 août 2026Fonctionnalité
  • Fixed the printed/digital receipt going nearly invisible in dark mode. ReceiptDocument (the shared component behind the public /r/[orderId] page and the receipt-settings live preview) deliberately renders as fixed black-on-white "paper" regardless of the app's theme — but a global dark-mode CSS rule that remaps .text-black/.text-gray-* to pale cream (for readability against the dark dashboard theme) was catching it too, since it wasn't opted out. Fixed by applying the same print-report marker class already used by the other "always white" views (print-report-shell.tsx, order-history-print-view.tsx) and extending that opt-out rule to cover the additional gray shades the receipt uses.
  • Added a printer-menu "Reprint Last Order" action plus a direct link to Order History (?tab=history) for reprinting anything older — the last completed order's ReceiptData is now kept in a small persisted store (useLastReceipt) instead of living only inside the checkout dialog's local state and disappearing once it closed. Order History itself gained a genuine "Reprint" action (prints via the paired Bluetooth printer, not just the "View Receipt" web link), backed by a new GET .../pos/orders/[orderId]/receipt endpoint that returns the same ReceiptData shape used everywhere else.
  • Fixed the POS cart's Pay button requiring a scroll to reach. Root cause: the shared dashboard PageShell wraps every page's content in a plain, unbounded-height div inside its own scrollable region — fine for ordinary pages that scroll at the page level, but it meant PosShell's flex-1 (which expects a bounded-height flex parent so its own internal item-grid/cart scroll regions can work) had nothing to size against, so the whole POS page just grew to fit content instead of keeping the cart footer pinned. Gave that wrapper min-h-full — additive for every other page, and the fix PosShell needed to properly fill the viewport and let only its item grid and cart list scroll internally.
2.31.0 — 8 août 2026Fonctionnalité
  • Redesigned the printed thermal receipt and fixed a real cropping bug. buildEscPos()'s store name was hard-truncated with .substring(0, 16) regardless of paper width — a store named "TAHOMA CAFE & EATERY" printed as "TAHOMA CAFE & EA", cutting off mid-word. The same truncation hit item names and order notes. Replaced with a proper wrapText() word-wrapper everywhere free text is printed, so long content wraps onto extra lines instead of silently disappearing. Also widened the paper feed before the cut command and added an explicit tear-guide line — thin feed was letting consecutive orders visually run into each other on cutter-less printers.
  • Added a Receipt & WhatsApp settings card (Profile page) with an editable footer message, Facebook handle, social-links visibility, and a live receipt preview — reusing Instagram/TikTok/tagline from the store's Storefront where one exists, so there's no duplicate data entry for a cash-only store.
  • Automated customer-facing digital receipts. New public, unauthenticated /r/[orderId] page (works for both storefront and walk-in POS orders) renders the same receipt data as a shareable, printable page. Once an order's payment is confirmed and a customer phone number is on file, a WhatsApp message with the receipt link now sends automatically (Fonnte) — gated by a per-store on/off toggle. Every send attempt is logged (OrderReceiptSend) and surfaced as a "View Receipt" / "Send via WhatsApp" action with sent/failed status in both POS order history and the storefront's own order-status page, with a manual resend option.
2.30.0 — 8 août 2026Correctif
  • Fixed a currency-conversion bug that mis-priced menu items and revenue figures for every non-IDR store — surfaced as a EUR store's ~€1 product showing as "€20,833.33" in the POS menu. Root cause: Product/Material costs are stored in IDR (the platform's base currency) everywhere, but several code paths copied that raw IDR number directly into places that must hold a literal value in the store owner's own currency, without converting first.
2.29.0 — 8 août 2026Fonctionnalité
  • Finance Reports overhaul: fixed a silent-failure bug, and shipped discount/refund tracking, four new report types, and a multi-outlet P&L rollup. Root cause of "the margin/loss cards are gone": the KPI grid and every report tab rendered {data && (...)} with no error branch — a failed or slow query didn't show an error, it just silently disappeared. Every tab now shows an explicit loading skeleton or a retryable error state instead.
2.28.0 — 8 août 2026Fonctionnalité
  • Bulk "Add to Menu" on the Products list, and a fix for products that silently never made it into the POS menu. Selecting products in bulk-select mode now shows an "Add to Menu (N)" button alongside bulk delete — it skips products already linked, resolves each distinct category once up front (so a batch sharing a category doesn't race and create duplicates), then links the rest in parallel and reports a single summary toast. Root-caused the underlying sync gap: a new store's first-ever storefront record was created via a find-then-create check that two near-simultaneous product creations could both pass, so the loser's create threw a unique-constraint error that was silently swallowed — that product's menu item was just never made, with no visible failure anywhere. getStorefrontByStoreId now upserts on storeId instead, so the second caller becomes a no-op update rather than a failed create. The new bulk action doubles as the recovery tool for any product that still falls out of sync for other reasons — select it and re-add.
  • The Data page (Raw Materials / Recipes / Products / Suppliers) now remembers where you left off, matching the pattern already used on Stock/History. The active tab persists to localStorage and syncs to a ?tab= URL param (deep-linkable, wins over the saved value); each tab's own filter/sort selections (category, department, stock status, sort order, page size) persist the same way, scoped per store. Free-text search and pagination position are deliberately excluded so a reload never lands on stale search text or an out-of-range page.
2.27.0 — 8 août 2026Correctif
  • Fixed a hydration-breaking bug on the Work Schedule page: the staff/shift-block filter popovers and the "Apply Template" staff checklist both nested a <Checkbox> (which renders as <button role="checkbox">) inside an outer <button> — invalid HTML that broke hydration and left the filter dropdowns non-functional. Both now use a role="button" div with keyboard support instead.
  • Dates now render in the user's selected language everywhere, not just translated UI text around them. Found the root cause: two competing date-formatting utilities existed (lib/utils/formatting.ts, locale-aware but silently defaulting to English whenever a caller forgot to pass a locale — which was almost every caller; and lib/utils/format-date.ts, hardcoded to en-GB with no locale concept at all), plus ~15 files calling .toLocaleDateString()/.toLocaleString()/.toLocaleTimeString() directly with a hardcoded or omitted locale, plus every one of the ~29 calendar popups across the app never passing a locale to the shared <Calendar> primitive (so month/weekday names always showed in English regardless of app language).
2.26.1 — 8 août 2026Infra
  • Two dashboard pages (Finance Reports, Dashboard Analytics) were eagerly bundling the `xlsx` library just for an "Export as Excel" button. Every other export surface in the app already dynamically import()s xlsx/jspdf on click rather than on page load (see src/lib/utils/export.ts); these two hand-rolled exports had drifted from that pattern. Switched both to a dynamic import, matching the rest of the codebase — trims unnecessary JS off the initial bundle for two pages every merchant hits regularly, no behavior change.
2.26.0 — 8 août 2026Fonctionnalité
  • Management is now a Stock-focused page; Alerts is signal-only. Management's five tabs (Deliveries, Production, a mislabeled "History" that was actually production-batch history, Stock, Movements) are down to two: Stock and History. Stock now covers the full lifecycle — a live per-item stock-reduction bar (Progress component, colored by severity, realtime via the existing STOCK_CHANGED channel), a new editable expiration date per material (single field, not per-batch/lot), waste/condition recording (existing WasteEntry reasons, now front-and-center), search/status/category/expiration filters with click-to-sort, and a "Reorder & Deliveries" sub-tab that absorbs the old Deliveries tab's PLACED→RECEIVED tracking plus Alerts' former "Create Order"/"Bulk Order" dialogs and "Orders to Place" view. History is the renamed Movements tab (the real stock-change ledger) with an added date-range filter and sort. Alerts drops the ?view=orders toggle entirely — it's just the low-stock table and unpaid-orders card now, and its supplier action buttons deep-link into Management's Stock tab (?tab=stock&highlight=<materialId> or &supplierId=<id>), which auto-opens the matching reorder dialog on arrival.
  • Send a supplier order's PDF detail directly from the Stock tab — by email (Resend, as an attachment) or WhatsApp (Fonnte, as a document link), pick one or both. A local-download alternative uses a new dedicated print page (/management/print, window.print() → Save as PDF) following the same pattern already established by pos/orders/print/attendance/print, replacing the older jsPDF-dialog download this superseded.
  • New optional Production page (/production), off by default per store. Recipe-to-batch production (the old Production + Production History tabs, moved wholesale into a new production feature) is a distinct manufacturing workflow that not every merchant needs — many cook fresh to order with no fixed recipe. The page shows a guide explaining what it's for before the owner opts in via a toggle; enabling reveals the moved Produce/History sub-tabs.
  • Filters and the active tab are now remembered per device. Stock, History, and the Management tab bar all persist their last state to localStorage (reusing/relocating the POS feature's existing usePersistedState hook to src/lib/hooks/, now shared instead of POS-only) — returning to the page lands back where you left it. Deep-link URL params always win over the saved state.
  • New shared FilterBar component (search + filter controls + clear button) factored out for Stock/History to share instead of each hand-building its own layout.
2.25.0 — 8 août 2026Fonctionnalité
  • Work Schedule: a custom-range roster grid, filters, and a bulk "Apply Template" action. The Block Name field in Manage Shift Blocks now shows a "e.g. Morning Shift" placeholder. The roster grid gained a staff filter and a shift-block filter (both above the table) — the staff filter narrows which rows show, the block filter dims/excludes non-matching entries within a row while keeping every staff row visible, since the point of "who's on Morning this week" is seeing who's not on it too; day-off entries stay visible under a block filter since they're informative, not noise. The fixed Mon–Sun week is now a picker built on the shared DateRangeField (the same range calendar used elsewhere in the app), constrained to a 7-day minimum so it can't shrink below a normal roster cycle — the grid's day columns, and its width, now track exactly the chosen range instead of always rendering 7. Prev/next now pages by the current range's length, and "Today" resets to the default Mon–Sun week. Both filters persist across range navigation on purpose (a manager filtering to one block or person is usually paging through several ranges with that same lens). Also added an "Apply Template" dialog next to Manage Shift Blocks: pick one shift block, check staff (default none, "Select all" shortcut) and days of the visible range (default weekdays), and Save creates every resulting roster entry in one go via the existing-but-previously-unused POST /staff-schedules/bulk endpoint, chunked at 200 entries per request to stay under its validation cap. Staff/day pairs that already have an entry that day are skipped rather than duplicated, and the result toast reports how many were applied vs. already scheduled. Previously every cell had to be assigned one at a time via the single-cell dialog. Bulk-applied entries land as Draft like any manually-created entry and still need Publish.
  • Fixed the "standard work minutes per day" field rendering as a 12-hour clock picker (e.g. "08.00 AM"). It's a duration (how many hours count as a normal day, for the overtime split), not a time of day — the AM/PM suffix implied a shift start time that didn't exist. Replaced with a plain hours + minutes counter (e.g. "8 h 0 m"), matching the "Xh Ym" format already used for the Regular/Overtime columns in the same table.
2.24.1 — 8 août 2026Correctif
  • Subscription plan price was hardcoded in Rupiah, ignoring the owner's chosen display currency — and stale against the public /pricing page besides. The in-app Billing page and the Profile page's Subscription card both showed a static Rp 99.000/Rp 249.000 string for the POS/Operations plan regardless of locale or the account's currency setting — a USD-currency owner on the French UI saw "Rp 249.000/mois". Both surfaces now derive the price from the plan's IDR base rate through the same live exchange-rate conversion (useCurrency().formatPrice) already used everywhere else in the dashboard, so it renders correctly in whichever of the ~140 supported currencies the owner has selected. The Profile card previously supported only IDR/USD/EUR/MGA and silently fell back to Rupiah for every other currency; it now works for all of them. The base itself was also wrong: Rp 99.000/Rp 249.000 predates a price raise applied to the public /pricing page (now Rp 229k/Rp 459k, i.e. $14.99/$29.99) — the Billing/Profile surfaces were never updated when that raise shipped. Both now use the same Rp 229.000/Rp 459.000 base as /pricing. Removed the now-dead hardcoded Rp .../month translation strings and the finite PLAN_PRICES currency lookup table that caused this. Also found and fixed the same staleness on the actual /payments checkout summary: id.ts/fr.ts's pricing.plans.starter/pro.price (read by payment-summary.tsx) still quoted the old Rp 99.000/249.000 and 9,99 €/24,99 € — a checkout screen showing a different price than the pricing page just clicked from. Corrected to match /pricing's Rp 229.000/459.000 and 13,99 €/27,99 €.
2.24.0 — 8 août 2026Correctif
  • Every image upload now guarantees a compressed output instead of trusting the client. Added a server-side compression pass (compressImageServer, using sharp) inside /api/upload: every accepted image is resized to at most 1600px on its longest edge and re-encoded until it's at or under a target size (2MB by default; a feature can request a larger target, e.g. the storefront cover banner's 5MB, via a maxSizeMB form field, clamped server-side to a safe range). This is now the authoritative guarantee — client-side compression (compressImage()) remains a bandwidth/UX optimization on top of it, not the only enforcement point.
  • Fixed a real bug: the shared <ImageUpload> component (used for storefront logo, cover banner, and menu item photos) rejected the raw selected file against the feature's target size (e.g. "must be under 2MB" for a logo) before attempting compression — so a normal 3–4MB smartphone photo was rejected outright even though compression would have shrunk it comfortably under the limit. Raw files are now checked only against a generous processing-cost ceiling (5MB); the target size is what compression aims for, not a rejection threshold.
  • Attendance selfie capture had no compression or validation at all (selfie-capture.tsx + clock-in-out-dialog.tsx) — camera captures and the file-picker fallback now run through compressImage() before upload, same as every other image surface.
  • Centralized the size/dimension rule in src/lib/constants/image.ts, shared by both the client util and the new server util, so the two layers can't drift apart. Documented the rule in AGENTS.md ("Images" under Coding rules) so future image-upload features follow the same pipeline instead of reinventing size limits.
2.23.0 — 7 août 2026UX
  • Logout consolidated into one menu, for both personas — not scattered across a standalone topbar button and a PIN-based switcher. While acting as a staff persona, the account dropdown previously had no logout option at all — the topbar's real "Logout" button hid itself in that state (it signs the underlying owner account out entirely, which a cashier shouldn't casually trigger), and "Back to Owner Account" is a PIN-gated switch, not a logout. The standalone topbar Logout button is gone; every logout-shaped action now lives in the one account dropdown, for Owner and staff alike:
  • Zero-staff stores skip the picker entirely. StoreAccessGate (and /pos's own gate, kept consistent with it) now also bypasses when a store has no active non-owner staff members — with nobody else to choose from, the "who's using this device?" screen had nothing real to offer and was just an always-the-same-answer click every time a solo-operator store (on OPERATIONS+, past the FREE/POS bypass) was opened from /stores.
2.22.0 — 7 août 2026Fonctionnalité
  • OS-level Web Push notifications — new storefront orders and low/critical stock now alert even when the tab is closed or the screen is off. New optional VAPID_* env vars enable real browser/OS push (standard Push API, not Firebase) delivered through the existing PWA service worker. A new toggle in the NotificationBell popover ("Enable"/"Disable") lets any device — owner or staff persona — subscribe; delivery fans out per-store to every subscribed device, since a shared shop device operated via staff PIN carries no independent User.id to key against. Dead subscriptions (expired/unsubscribed — common on iOS Safari, which rotates these more aggressively than Chrome) are cleaned up automatically on the next failed send (404/410).
  • Storefront orders now also live-update open dashboards. Fixed an adjacent gap found while wiring this up: POST /api/public/orders never called the existing Pusher publishStoreEvent(ORDER_CREATED) that the POS order route already fires — a storefront order previously only reached an open dashboard tab on its next 30s notification-bell poll. Both routes now match.
  • Fixed: `/sw.js` and the PWA manifest were unreachable for anyone without a login session. src/proxy.ts's auth matcher excluded favicon.ico and image extensions from the login redirect, but not sw.js/manifest.webmanifest — so any first-time visitor to the public storefront (never logged in) got an HTML login-redirect page back instead of the actual service worker file, silently breaking PWA install/offline support and, now, push registration for that entire audience. Found by testing this feature live against a running server, not just via type-check. Logged-in dashboard users were unaffected (they always carry a session cookie).
  • Graceful degradation, same pattern as the rest of the realtime layer: until an operator sets VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY/VAPID_SUBJECT/NEXT_PUBLIC_VAPID_PUBLIC_KEY, the push toggle stays hidden entirely and nothing changes — same "no-op, fall back to polling" contract as publishStoreEvent().
  • Cross-device/browser handling: iOS Safari requires the site to be added to the Home Screen (iOS 16.4+) — the toggle shows an explicit "Add to Home Screen" hint there instead of a dead-end "unsupported" state, since Apple gates the whole Push API behind standalone install. A previously-denied browser permission is surfaced as its own "blocked" state (browsers never re-prompt after denial) rather than a retry button that would silently no-op.
  • New src/lib/push/ (VAPID-configured send helper, mirrors src/lib/realtime/publish.ts's graceful-degradation pattern), src/hooks/use-push-notifications.ts, PushSubscription table, and push/notificationclick listeners in public/sw.js.
2.21.0 — 7 août 2026Fonctionnalité
  • Shifts, Schedule, and Attendance merged into one page. /shifts (till cash sessions) and /attendance (clock-in/out audit log) were separate nav items with no owner-vs-staff view split — a staff member granted either page saw the same full manager table an owner would, and by default couldn't reach either at all. Both are now folded into /schedule, which already split by role: the manager view gains a Log & History section merging clock events and till cash open/close (shown as Cash In/Out) into one chronological, filterable-by-staff/type/date-range timeline; the staff view gains Clock In/Out on every staff member's page, plus Cash In/Out for POS-capable roles (Cashier, Owner/Manager) only, plus a self-service My History. /shifts and /attendance now redirect to /schedule rather than dead-ending old links. No schema change — Shift and AttendanceRecord stay two separate, independently-audited tables; only the UI merged, via a new fetchUnifiedLog()/mergeUnifiedLog() (the latter pure and unit-tested) reused by the new Log tab, the staff History section, and the existing attendance PDF export (which now includes Cash In/Out rows too).
  • Roster: one Publish button, not two. "Publish Week" and "Print / Export PDF" collapse into a single dynamic action — "Publish Week" while the visible week has any draft entry, "Published" (muted, still clickable) once every entry is published, and clicking it in that state opens the PDF export instead of re-publishing. The week grid's day headers also show the weekday name (Mon, Tue, …), not just the date.
  • No more double PIN, extended to till cash too. Following up on the clock-in/out PIN dedup shipped earlier this session, opening a till (POST /stores/[id]/shifts) now also accepts an already-active StaffSession as proof instead of always re-asking for the PIN.
  • "Clock In / Out" removed from the account dropdown (now lives on the Schedule page) — replaced with "Account Access", a small dialog showing which persona is signed in on this device and exactly which pages it can reach. Built as a dialog rather than a Profile page section: /profile is hard-gated owner-only, so a staff persona could never have reached a card placed there.
2.20.0 — 7 août 2026Fonctionnalité
  • Live push layer (Pusher Channels) — orders, menu, materials, products, recipes, and stock now update across tabs/devices without waiting for the next poll. Every write that matters (POS order create/status-change/finalize, material/product/recipe create/update/delete, stock adjustments and sale deductions) now fires a lightweight {action, entityId} event on the store's private channel via a new non-blocking publishStoreEvent() helper; the affected list/query on any other connected tab or device invalidates and refetches immediately instead of waiting up to 5–10s for its existing poll tick. The old aggressive polling intervals are left in place as the fallback, unchanged — this is additive, not a replacement.
  • Presence. New <PresenceAvatars> component + useStorePresence() hook show who else is currently connected to a store (owner or PIN-logged-in staff) via a Pusher presence channel — an initials avatar stack with name tooltips, renders nothing when nobody else is online.
  • Graceful degradation by design (AGENTS.md §6): none of this requires setup to keep working. Until the operator sets PUSHER_APP_ID/PUSHER_KEY/PUSHER_SECRET/PUSHER_CLUSTER (see .env.example), publishStoreEvent() silently no-ops and every surface behaves exactly as before — polling only, no live push, no presence. The one exception is the POS order queue, which already had a custom SSE endpoint (orders/stream) for this; it now prefers Pusher when configured and falls back to that existing SSE mechanism unchanged when it isn't, rather than running both at once.
  • New src/lib/realtime/ (channel/event naming, server + client Pusher singletons, the publish helper) and POST /api/pusher/auth (channel authorization — accepts either an owner/manager session or an active staff PIN session, matching the existing dual-auth pattern already used by staff-gated POS routes).
  • Menu-item-level push coverage is partial: a product's price/name/department change publishes menu.changed (since that syncs to its linked MenuItem), but direct storefront/menu-editor CRUD on MenuItem (add/remove/reorder categories and items outside of a linked product) doesn't publish yet — POS menu still catches those via its existing 5s poll, just not instantly. Full storefront-item-level coverage is a follow-up.
  • No presence beyond "who's connected" — no live cursors, no per-field "someone else is editing this record right now" indicator, no conflict resolution for two people editing the same record at once (still last-write-wins). True field-level collaborative editing (the deeper Notion/Figma comparison) is out of scope for this pass.
  • Only the six domains above publish events. Tables, reservations, supplier orders, alerts, schedule, finance, and admin dashboard remain poll-only for now — next in line by current poll aggressiveness (10–60s tier).
2.19.0 — 7 août 2026Fonctionnalité
  • Store access gate: pick Owner or Staff before the dashboard, not just before POS. New StoreAccessGate, generalized from the existing POS staff picker, now wraps the entire (dashboard) route group — not just /pos — so a shared/unlocked device shows a "who's using this device?" checkpoint before any page renders, dashboard-wide. Picking a staff member reuses the same PIN as everywhere else; picking "Continue as Owner" now requires the separate Owner PIN (prompting to set one first if none exists yet) instead of trusting an already-open Better Auth session, since that session is exactly what a shared device leaves sitting unlocked. Skipped entirely on FREE/POS plans (no staff feature to gate). Once chosen, the same usePosSession state POS itself reads means /pos won't ask again on top of this.
  • Fixed: "Continue as Owner" didn't clear a leftover staff session. Both the new gate and the existing POS gate's "Continue as Owner" only updated client-side state — a StaffSession cookie left over from an earlier persona (or another tab) would still be read by the server-side owner-only-page guard and bounce the request right back out. Both now clear it first (same call the existing "switch back to Owner" flow already made).
  • Clock-in/out: no more double PIN. A PIN is meant to prove identity once, at login — if a device is already operating as a specific staff persona (via the gate above or "Switch to Staff Account"), the Clock In/Out dialog now skips straight to picking clock-in/out for them instead of asking to re-select their name and re-enter their PIN. The kiosk-style "pick anyone, enter their PIN" flow is unchanged for devices with no active persona. Enforced server-side too: clock-in/clock-out/absence now accept an active StaffSession as proof (new isStaffAuthenticated() helper), not just a submitted PIN.
  • POS: periodic PIN re-verification. Because POS handles cash, a staff PIN there now expires after 4 hours of continuous use and re-prompts for the same PIN (not a full re-pick) — separate from, and on top of, the once-a-day session everywhere else. Doesn't apply to the Owner persona.
  • Clock-in/out: history log + photo retake. The dialog now has a read-only history view of a staff member's own recent clock-in/out/absence records (timestamp, selfie thumbnail, location). Nothing in it is editable except the photo, and only within 30 minutes of the original capture — a retake replaces the selfie on that same record (via a new POST .../[attendanceId]/retake-photo, itself PIN/session-checked and window-checked) rather than creating a new entry, so a blurry first shot doesn't leave a duplicate in the log. Everything else about a record (type, timestamp, notes) stays immutable, same append-only-audit-trail principle as the existing manual-close correction flow.
  • Selfie capture: hardened for desktop and non-mobile browsers. Distinguishes camera-permission-denied, no-camera-found, and insecure-context (non-HTTPS/non-localhost) failures with a specific message for each instead of one generic one, and adds a "Try again" retry button so granting the permission via the browser's own UI doesn't require closing and reopening the whole dialog. The facingMode: "user" constraint was already a soft/"ideal" hint (works on desktop webcams that don't report a front-facing camera), and the file-input fallback already degrades correctly to a normal file picker on desktop — this pass is about clearer failure states and recovery, not a new capture pipeline.
2.17.3 — 6 août 2026Correctif
  • Pricing page now matches what's actually shipped. Audited every feature claim on /pricing (tier cards + the detailed comparison table) against real plan gates in the code. Removed five claims that don't exist in the product (a daily P&L email, allergen/nutrition labels, a wholesale order portal, SSO, and a public API/Zapier/webhooks integration) and fixed a self-contradiction where the Operations card advertised a "multi-outlet dashboard" while the comparison table marked the same thing Enterprise-only — both were partly right: Operations gets unlimited outlets, but the cross-store owner roll-up dashboard (/owner) is genuinely Enterprise-exclusive; copy now says so distinctly instead of using the same ambiguous label for both. Added the three real, previously-unadvertised Operations-tier features shipped this week — shift scheduling & rosters, selfie + geolocation attendance, and waste/loss tracking — plus a reservations mention on the POS tier. Removed four dead, never-rendered i18n feature strings left over from an earlier card layout.
2.17.2 — 6 août 2026UX
  • One integrated date-range picker, everywhere. Every date-range filter across the dashboard — Finance, Dashboard Analytics, Attendance, Owner Dashboard, and POS Order History — now opens the same single calendar popover (DateRangeField) instead of a pair of separate From/To date inputs. Quick-pick shortcuts (Today, Yesterday, Last 7/30 Days, This Month) live in the same popover rather than a second dropdown next to the inputs, so picking a range is one click into one control instead of juggling three. POS Order History keeps its own outer preset selector (with its wider "All time"/"Last month" options) and uses the new picker just for its "Custom" calendar step. Removed the now-redundant DateRangeLabel component.
2.17.1 — 6 août 2026UX
  • Schedule: "Today" shortcut, day-off marking, and a print/PDF export. The week grid now has a one-click jump back to the current week, a "Mark Day Off" toggle per staff/day (shown distinctly from a working shift in the grid and in staff's own "My Schedule" view), and clicking a date header opens a day-at-a-glance detail view. Publishing a week now also opens a printable roster (branded with the same Epidom header/watermark/footer as other reports) — "printing" is how the exported PDF is produced (browser Print → Save as PDF), matching the existing Order History report's pattern rather than a separate one-off PDF pipeline.
  • Attendance: date-range presets and a print/PDF export. The From/To inputs are replaced with the same integrated calendar-and-presets picker (Today, Yesterday, Last 7/30/90 Days, This Month, This Year, or a custom range) used on the Finance report, plus a "Print / Export PDF" action that exports whichever tab (Log or Hours & Overtime) is currently open.
  • Fixed: "pages.noData" showing as literal text instead of translated copy on the Attendance page — the key existed nested under pages.analytics.noData, not at the top level pages.noData the new page actually called; added the missing top-level key to both id.ts and en.ts and audited every other new i18n key introduced by the staff-scheduling/attendance feature against both locale files (all resolve correctly now).
  • Overtime threshold is now a time input, not raw minutes. "Standard work minutes per day" was a plain number field requiring mental math (e.g. typing "480" for 8 hours) — now an HH:mm time picker, converted to minutes only at the API boundary.
  • New StaffSchedule.isDayOff field (migration 20260805230007_add_staff_schedule_day_off), mutually exclusive with the existing named-block/custom-time choice (enforced in Zod).
2.17.0 — 6 août 2026Fonctionnalité
  • Order-linked production. When a Kitchen & Bar order needs more of a recipe-linked product than is currently sitting on hand, the system now auto-drafts a production task the moment the order enters the kitchen queue — visible on the KDS board with a "making" indicator on that ticket — instead of running as a disconnected, staff-triggered-only workflow. Tapping the item to Ready on KDS completes that task in the same action; no separate "go complete the batch" step.
  • Management's Production History now shows where each batch came from — a "Manual" badge for the existing proactive/ahead-of-demand flow (unchanged), or "From Order #…" for one auto-drafted by a live order, with the linked order number for traceability.
  • Known v1 limitations: one auto-drafted batch per shortfall-triggering order — near-simultaneous orders for the same out-of-stock product each get their own batch rather than being consolidated (batch sprawl during a rush is possible); order cancellation doesn't currently auto-cancel a still-open linked batch; only the primary order-confirmation paths (POS cash/pay-later, POS finalize, and Xendit online payment) trigger the auto-draft — aggregator-imported orders and other less common entry points don't yet.
2.16.0 — 5 août 2026Fonctionnalité
  • Staff Scheduling. Managers can build and publish weekly work rosters from a new /schedule page — pick from reusable named shift blocks (e.g. "Shift 1" 08:00–16:00) or set a custom time per staff member per day, tag a Kitchen/Bar/Both department, and publish a week in one action. Staff (Cashier/Kitchen roles) see a read-only "My Schedule" list of their own upcoming published shifts.
  • Selfie + Geolocation Attendance. A new "Clock In / Out" action in the account menu (available to every staff persona, regardless of page permissions) captures a front-camera selfie and best-effort GPS location before recording a clock-in, clock-out, or absence report — falls back to the device's native camera picker if getUserMedia is unavailable, and never blocks the action if location permission is denied. Managers get a filterable audit trail (by staff and date range) on a new /attendance page, with each entry's selfie thumbnail and a "view on Google Maps" link (no embedded map — see AGENTS.md's no-maps-library rule).
  • Automatic Working Hours & Overtime. The Attendance page's "Hours & Overtime" tab pairs clock-in/out events into completed workdays (correctly attributing a shift that crosses midnight to the day it started) and splits worked minutes into regular vs. overtime against a configurable per-store daily threshold (default 8h). A stuck clock-in (forgotten clock-out) is flagged rather than guessed at, with a manager "close manually" recovery action that appends a correction record instead of editing history.
  • Shift-Block Revenue Report. A new "By Shift Block" tab on the Finance report sums revenue/order-count per named shift block per day, plus who was rostered on. Named blocks can deliberately overlap (staggered handover coverage), so this is a coverage-window report, not a partition — the UI explicitly discloses that totals across blocks aren't expected to sum to the grand total.
  • Known v1 limitations (see docs/roadmap.md): the overtime threshold is a single flat per-store value (no per-role/per-shift override); true order-level "who processed this sale" attribution only exists for cashiers via the existing POS till session (/finance/by-shift?staffId=) — for every other role, the new report shows "who was rostered on" as context, not a claim of who rang up a given order.
2.15.0 — 5 août 2026Fonctionnalité
  • Waste Management. Record wasted Materials or Products from Management → Edit Stock ("Record Waste") with a predefined reason (Expired, Damaged, Spoiled, Overproduction, Quality Control) or a custom free-text reason — each entry snapshots the item's cost and stores a computed loss value, and deducts current stock the same way a stock adjustment does.
  • Waste loss is now trackable on the Finance report. A new "Waste Loss" KPI card (and, alongside it, a "Net Profit" card that already existed in the export but had no card) shows the period's total loss, which now also reduces Net Profit. A new "Waste" tab lists every entry with a by-reason breakdown, and included in the Excel export.
  • Waste entries are correctable for any condition. Editing an entry (from the Finance Waste tab) reconciles current stock by appending a compensating movement rather than rewriting history, and an "Advanced" override lets a manager correct the recorded unit cost itself; deleting an entry restores the stock it had consumed.
  • Fixed: stock adjustments for Products silently failed. The "Adjust Stock" dialog has offered a "Product" option since it shipped, but the backend rejected it with "Product stock adjustment not yet implemented." Product stock adjustments (and the new waste feature) now work for both Materials and Products.
2.14.15 — 5 août 2026Fonctionnalité
  • Account deletion replaced with deactivation + a 30-day reactivation grace period. Profile → Account Settings' "Delete Account" is now "Deactivate Account": your data is never touched, and logging back in any time within 30 days offers a one-click "Reactivate My Account" button that instantly restores everything. Your public storefront(s) go offline for as long as the account stays deactivated, and come back automatically on reactivation.
  • 1-year data retention with support-assisted recovery. After the 30-day window, self-service reactivation ends but your data stays on file for up to 12 months — recovering it means contacting support for a case-by-case quoted fee, after which an admin can manually reactivate the account. Past 12 months, a new daily background job permanently and irreversibly purges the account.
  • Admin panel: reactivate deactivated accounts. The Master Admin Panel now shows a "Deactivated" stat tile and badge per user, plus a "Reactivate Account" row action that works at any point within the 1-year retention window. The existing instant hard-delete action is unchanged.
  • Updated Terms & Conditions and Privacy Policy with the full deactivation → reactivation → retention → deletion lifecycle, and explicit data-subject rights for France/EU users (GDPR, right to complain to the CNIL) and Indonesian users (UU PDP / Law No. 27 of 2022).
  • Fixed: Privacy Policy required login to view. /privacy was missing from the auth middleware's public-route allowlist, so visitors were redirected to /login instead of seeing the policy — defeating the point of a public privacy policy. Added alongside /terms.
2.14.14 — 5 août 2026Fonctionnalité
  • "Mark as Paid" now records how an order was settled. Every mark-paid action — the Active Queue order card, the Order History detail dialog and its bulk action, and the dashboard's Unpaid Orders alert — now opens a confirmation dialog to pick the payment method actually used (Cash, QRIS, GoPay, OVO, DANA, ShopeePay, Virtual Account, Credit Card) and add an optional note (e.g. "client paid directly to the owner") before settling the order.
2.14.13 — 5 août 2026Fonctionnalité
  • Enterprise "Custom Development" page. Under the dashboard's Enterprise section, request a custom feature or website build — describe your requirements, budget, and timeline. Submissions notify the founder/CRO team by email and appear in a new admin triage queue at /admin/custom-development. Users can edit or delete their own submitted requests.
  • Admin panel: Custom Development link + pending-request badges. The Master Admin Panel header now links to the Custom Development queue, and both it and the Feedback button show a live count badge for open/new items awaiting a first look.
  • Consolidated Tracking into Management. The Tracking page's unique stock-movement ledger is now a "Movements" tab inside Management; its redundant Stock Levels view (already covered by the Data page) was dropped, along with the separate Tracking nav item.
  • Fixed a tracking-page crash (Value 'BOTH' not found in enum 'Department') caused by an out-of-sync generated Prisma Client after the Material department field's BOTH option was added.
  • Delivery print dialog's "Export PDF" now actually exports a PDF (previously a stub); removed the dead, unreachable "Add Delivery" form mode.
  • Stock deduction now skips line items a KDS operator cancelled, so a cancelled item's ingredients/stock are no longer deducted when the rest of the order is delivered.
  • Fixed the pricing page's plan cards: CTA buttons, dividers, and feature lists across the four tiers weren't level with each other because the header tagline and price block varied in height per tier — now consistently aligned.
2.14.12 — 2 août 2026Fonctionnalité
  • Finance reports: filter/breakdown by category, department, and staff shift. The Finance page now has Staff and Category (menu category) filters, plus new "By Category," "By Shift," and "By Department" report tabs/cards — the last answering "how much did Kitchen sell vs. Bar today." All tables gained sortable column headers.
  • Kitchen/Bar department field, separate from the existing free-text category, on Raw Materials, Recipes, and Products — filterable and shown as a badge on the Data page, the Menu editor, and a new Kitchen/Bar toggle on the POS item grid. A Product's department automatically syncs to its linked storefront menu item, same as name/price already did.
  • Dashboard: "New Orders" card. Highlights orders awaiting confirmation (especially storefront orders) right on the dashboard, with a link straight to the Order Queue. Live-updates via the existing order SSE stream.
  • Dashboard & Finance date ranges now show a dynamic label ("Today," "Last 7 Days," "This Month," etc.) next to the date pickers, and the Dashboard's Analytics section now defaults to today instead of month-to-date.
2.14.11 — 2 août 2026Fonctionnalité
  • Fees & Taxes settings, per store. Added a "Fees & Taxes" card to each store's Profile page: a configurable tax rate (with an inclusive/exclusive toggle and custom label, e.g. "PPN 11%"), a separate service charge rate, and a payment-processing fee-rate table per method (QRIS, GoPay, OVO, DANA, ShopeePay, bank transfer, Stripe card), pre-filled with editable estimated default rates so a store gets accurate-ish reports even before touching the settings.
  • Financial calculations and reports now account for fees and taxes. Tax, service charge, and the estimated payment-processing fee are computed once per order (at POS checkout, hold/finalize, and storefront checkout) and frozen onto the order — so editing a store's rates later never rewrites past reports. The Finance page now shows Tax, Processing Fee, and Net Revenue KPIs, and the per-channel breakdown deducts tax and processing fee alongside aggregator commission; both are included in the Excel export.
  • The payment-processing fee is an estimate based on the merchant's configured rate, not a live reconciliation with Xendit/Stripe settlement data — Xendit's webhook payload doesn't carry the actual charged fee in this integration.
2.14.10 — 31 juillet 2026UX
  • Fix: "My tickets" crash for NEEDS_REVIEW status. Accounts with tickets transitioned to NEEDS_REVIEW by an admin were experiencing a render crash on the "My tickets" tab. Added the missing status to the frontend FeedbackStatus union type, the STATUS_BADGES map in the feedback dialog, and added corresponding translations (EN: "Needs review" / ID: "Perlu ditinjau").
  • Storefront settings: real-time auto-save. All fields in the storefront settings page now auto-save to the database 1.5 seconds after you stop typing. The manual "Save Settings" and "Cancel" buttons have been removed. A subtle "Saving..." / "Store settings saved." indicator appears at the top of the form.
  • QR code dialog: copy link. The shared QR code dialog (storefront, table QR, etc.) now shows the URL in a read-only input below the QR image, with a one-click copy button that briefly shows a green checkmark on success.
2.14.9 — 29 juillet 2026UX
  • Feedback dashboard: ticket detail modal. Every row/card now has an expand icon (and every board card is clickable) that opens a full-detail modal — user, page, full description, screenshot, dev note, and priority/status editing all in one place, without leaving the list.
  • Clickable status filters. The 5 summary stat cards (Open / In Progress / Review / Resolved / Archived) now double as filters — click one to narrow the list to that status, click again to clear it.
  • Type filter + search. Added a Bug/Feature/General type filter and a free-text search box (matches user, description, page, or ID) next to the view switcher.
  • Three layouts: Table / Board / Feed. Added a view switcher — Table (existing grouped list), Board (Notion-style Kanban columns by status, unaffected by the status filter beyond dimming the other columns so the full picture stays visible), and Feed (flat, newest-first card stream). The chosen view persists across visits via localStorage.
2.14.8 — 29 juillet 2026Fonctionnalité
  • Staff status management. The staff edit dialog now has an Active/Inactive control, guarded so the last active staff member or the store Owner can't be deactivated (would lock everyone out). The Owner role itself is locked from being changed and always appears pinned at the top of the list.
  • Role Access Details panel. The staff edit dialog now shows a reference panel describing exactly what each role (Owner / Manager / Cashier / Kitchen) can access.
  • PIN validation. The new-PIN field now requires exactly 4 digits before Save is enabled, with an inline error otherwise.
  • Menu item descriptions. Storefront Editor's Add/Edit item dialogs now have an optional description field (e.g. "orange, jasmine, espresso"); it renders under the item name in both the editor list and the POS product grid.
2.14.7 — 28 juillet 2026Fonctionnalité
  • Add Product dialog now shows a "Link to existing menu item" selector when the store has menu items not yet connected to any inventory product. Selecting one links the new product directly to that existing POS/storefront item instead of auto-creating a duplicate entry. Fully optional — leaving it on "Don't link" continues creating a new menu entry as before.
  • Added ?unlinked=true query param to GET /api/stores/[id]/storefront/items to return only menu items with no product association (used by the selector above).
2.14.6 — 28 juillet 2026Fonctionnalité
  • Added "Review" status (NEEDS_REVIEW) to the admin feedback tracker, sitting between In Progress and Resolved (styled purple). Stats card updated to show a 5th column for this status.
  • Added developer notes to each feedback entry: a private free-text field (saved as devNote in the DB) accessible only to admins. Inline pencil-to-edit textarea with save/cancel — visible below the description in both mobile and desktop views. Existing notes render with a violet 👁 badge and remain editable.
  • DB migration: 20260728083727_add_feedback_needs_review_and_devnote — added devNote String? to the feedback table and NEEDS_REVIEW to the FeedbackStatus enum.
2.14.5 — 28 juillet 2026UX
  • Added click-to-copy/bubble-to-copy to feedback description text. Clicking the description text now copies the full feedback text to the clipboard with a visual "Copied!" popover bubble (tooltip) and toast notification. Toggling expansion of long description texts is now handled by clear "Show more" / "Show less" links.
2.14.4 — 23 juillet 2026Correctif
  • Made the live "Change" preview in POS checkout recompute directly from the same field value driving the Amount Tendered input, in the same render pass, instead of a separate useWatch subscription — removes any chance of it lagging behind a keystroke.
  • Fixed wrong Change/Amount Tendered for stores on a non-IDR display currency (e.g. EUR) — every price in this system is stored internally in IDR and converted only for display, but the cashier's typed Amount Tendered was never converted back to IDR before being compared against the (IDR) cart total, so the preview showed a nonsense number, and — more seriously — the same unconverted value was being sent to the server and saved on the order. Amount Tendered is now converted to the base currency once, up front, before it's used anywhere (live preview, the printed receipt, and the API request). No effect on stores using IDR as their display currency (the common case), since that conversion is already a no-op there.
  • Redesigned the POS topbar for mobile: store name, connection status, staff name/role, and sign-out now collapse into a single profile icon menu on the left, and the cart button that used to float over the bottom of the screen now lives on the right side of the same topbar. Desktop is unchanged.
  • Added validation to POS cash checkout: Confirm Order is now disabled and shows an "insufficient amount" message whenever Amount Tendered is less than the order total, instead of allowing an order to be confirmed with negative change. Also enforced server-side, so a cash order can't be placed underpaid even by a direct API call.
  • Filled in the Meta Pixel side of the acquisition funnel (previously only Google Analytics was tracking most of these stages): signup now sends CompleteRegistration with real params instead of none; clicking a paid plan now also sends InitiateCheckout; activating the free plan now sends Lead; and checkout success now sends StartTrial for the 14-day POS trial or Subscribe for an immediate paid subscription, distinguished via a new trial flag threaded through the Stripe checkout success URL. Recurring-payment tracking from the Stripe webhook is server-side only and would need a separate Meta Conversions API integration — deferred for now.
2.14.3 — 23 juillet 2026Correctif
  • Fixed the POS mobile cart cropping the item list with no way to scroll to the rest. Root causes: the shared ScrollArea component was missing overflow-hidden (affecting every usage of it in the app), and a bottom-sheet drawer's height (auto clamped by max-height) doesn't reliably size a scrollable list through a nested flex layout. Switched the cart from a bottom-sheet drawer to a centered dialog — the same scrolling pattern already proven across every other dialog fixed this release — with its own header, scrollable item list, and a clearly separated totals footer.
  • Fixed the marketing site's mobile navigation drawer having no scroll region at all — a nav list taller than the screen had no way to reach the items below the fold.
  • Simplified the POS product grid and table floor plan on mobile phones: removed the bordered "card" look from product tiles (the color-coded table status borders are kept, just thinner, since that color is a status signal, not decoration) and tightened the grid spacing so more fits on screen at once.
  • Fixed the Tables page on mobile: the header (table count + "Download All Table QRs" + "Add Table") crammed into one row and wrapped into an unreadable multi-line column — it now stacks cleanly on narrow screens. Fixed the Reservations section's refresh button getting visually stranded on its own line once the status filter chips wrapped, by anchoring it to the section title instead. Tightened side padding throughout both sections on mobile.
  • Fixed each table card's QR/edit/delete icons overlapping the table label — they were absolutely positioned over the card with no reserved space, which collided with the label below on narrower cards. They're now a normal row above the label instead, so they can't overlap it.
  • Removed the left/right padding and card box around the Storefront Settings' Opening Hours rows on mobile — each day now runs edge-to-edge with a simple divider between days, instead of a boxed card floating with wide empty margins on both sides. The boxed look returns at sm: and up.
  • Removed the shared Card component's default padding and inter-section gap entirely on mobile phones app-wide (was p-6/gap-6 everywhere, phone or desktop) — every screen built on a Card/CardHeader/CardContent/CardFooter (Storefront settings, Dashboard data sections, account settings, and more) now uses the full screen width on a phone, with the normal spacing returning at sm: and up. Individual sections that already set their own padding are unaffected.
  • Removed the POS product grid's remaining left/right container padding and the gap between product tiles on mobile — the scrollable area now only has top spacing (pt-2), so tiles run flush to the screen edge and to each other instead of floating with margins on every side.
  • Fixed the Storefront editor's loading skeleton overflowing narrow phones — it used fixed pixel widths (w-96) sized for desktop instead of shrinking with the viewport, pushing part of the placeholder off-screen while the page was loading.
  • Capped the dashboard's shared page padding (PageShell, used by every dashboard page — Data, Tables, Staff, Storefront settings, and more) at p-2 on mobile phones, down from p-4 — one more source of wasted margin on every dashboard screen, not just the ones touched individually above.
  • Restored a border on the POS product grid's tiles on mobile — with zero gap between them and no image on some items, there was no visible boundary at all between adjacent products. The border now doubles as the tile spacing, reading as a clean grid instead of floating text.
  • Fixed a console accessibility error on the POS mobile cart ("DialogContent requires a DialogTitle") from switching it to a dialog earlier this release — it has its own visible header already, so this is a screen-reader-only title/description rather than a duplicated one on-screen.
  • Fixed Confirm Order and Hold not submitting at all on POS Checkout, POS Hold, Table create/edit, Add Staff, and Open/Close Shift — introduced earlier this release while adding scroll-safety to these dialogs. The submit button was wrapped in a <form> placed around the dialog, but dialog content renders through a React Portal to a different part of the page, so that <form> never actually contained its own submit button in the real DOM — clicking it did nothing. Fixed by using the same form="id" attribute binding (works regardless of DOM position) already used correctly everywhere else in the app.
  • Silenced a Postgres driver deprecation warning on server startup ("SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full'") by making the database connection string's SSL mode explicit (sslmode=verify-full) instead of relying on the implicit alias. No behavior change today — this is exactly what require already resolves to — but it keeps the connection secure once a future major version of the Postgres driver changes what the implicit modes mean.
  • Added a ninth Enterprise plan feature to the pricing page: custom integrations and bespoke website builds for restaurants/cafés with specific needs beyond the standard plans.
2.14.2 — 22 juillet 2026Correctif
  • Fixed the POS checkout "Change" amount not always updating live as Amount Tendered was typed — switched to React Hook Form's useWatch, the more reliable pattern for a value that needs to recompute on every keystroke.
  • Fixed checkout and Hold showing a generic "Failed to create order"/"Failed to hold order" toast with no way to know what went wrong. The API already knew the real reason (most commonly: resuming a held order after one of its items was deleted or taken off the menu) — the dialog was discarding it before showing a toast. It now surfaces the server's actual reason, naming the exact item(s) that are no longer available so the cashier knows what to remove from the cart.
  • Fixed several dialogs across POS and the dashboard (checkout, hold order, staff, shifts, account settings, menu editor, and more) that had no internal scroll region — on a phone, content taller than the screen (e.g. a Notes field at the bottom of checkout) simply ran off the bottom with no way to scroll to it. They now use the same bounded-height, scrollable-body pattern already used elsewhere in the app.
  • Simplified the mobile phone layout across POS, the customer storefront menu, and dashboard list views (Materials, Products, Recipes, Suppliers, Staff, Shifts, Feedback): removed boxy card borders/shadows/heavy padding in favor of a flatter, whitespace-separated list, and shrank secondary/metadata text (timestamps, emails, descriptions) so primary content stands out more. Desktop and tablet views are unchanged — this only applies below the sm breakpoint.
  • Fixed the POS mobile cart's "Clear All" button overlapping the sheet's own close (X) button — the sheet's close button floats in the top-right corner regardless of content, so the cart header now reserves room for it on mobile.
  • Fixed the POS header's date/time and staff badge overlapping the store name on narrow phone screens — the header has a fixed height, so text that didn't fit was wrapping and getting clipped instead of showing cleanly. The clock is now hidden on mobile (redundant with the phone's own status bar) and the staff name truncates instead of overflowing.
  • Tightened the Storefront Settings "Online Orders" / "Table Reservations" toggle cards on mobile — removed the box border/rounded corners/padding in favor of a plain divided list, matching the flattened style used elsewhere.
2.14.1 — 22 juillet 2026Correctif
  • Added Credit Card as a POS payment method, alongside Cash/QRIS/e-wallets/Virtual Account.
  • Fixed the POS cart failing to Pay or Hold again after resuming a held order, with visibly wrong totals (e.g. a subtotal in the billions). The API endpoints powering the order queue were sending raw database price values straight to the browser instead of converting them to plain numbers first — the browser received them as text, so every total calculation silently glued numbers together as strings instead of adding them.
  • Fixed the storefront's checkout button becoming unreachable when several items were in the cart — the same underlying viewport-height issue from the last release, found in more places (POS's mobile cart sheet and about a dozen other dialogs across Data, Recipes, Suppliers, and Management) and fixed everywhere at once via the shared dialog component.
  • Fixed table action buttons (QR code, edit, delete) and a notification's dismiss button being completely unreachable on iPad/touch devices — they were only ever shown on mouse hover, which touchscreens don't have. They're now always visible.
  • Enlarged several more touch targets that were under Apple's recommended minimum tap size across POS (staff switch, table actions, reservation actions).
  • Fixed the desktop POS cart's Pay button becoming unreachable with enough items in the cart, with no way to scroll down to it. A shared layout container was missing min-height: 0, a CSS default that (combined with a scrollable flex container) otherwise lets content grow past its allotted space instead of scrolling within it — silently clipping the bottom instead of showing a scrollbar. Fixed at the shared layout level, so this can't recur on any dashboard page, not just POS.
  • Fixed the desktop POS cart's Pay button overflowing past the panel's right edge regardless of total size — it was sized to 100% of the row's width while sitting next to the Hold button, which a plain width percentage doesn't account for. It now correctly grows to fill only the space actually left over, and wraps onto a second line for a very large total instead of cropping.
  • Fixed the desktop POS cart panel itself getting squeezed/cropped on the right — the menu grid next to it had the same missing "shrink below natural width" behavior horizontally that the scroll bug had vertically, so it could refuse to make room for the cart's fixed width. The cart panel now always keeps its full width regardless of the menu's content.
  • Audited the rest of the app for the same iPad/touch issues found above and fixed what else turned up: the Create/Edit Store dialogs had the same viewport-height bug, and a toast notification's dismiss button plus the "remove photo" control on every image upload (menu items, profile photo, etc.) were hover-only and unreachable on touch, same as the table buttons.
  • Documented all of these as explicit rules in the project's coding guidelines, so future changes get checked for them upfront instead of being caught one bug report at a time.
2.14.0 — 20 juillet 2026Fonctionnalité
  • Data → Materials: added Purchase Quantity + Purchase Price to raw materials and their per-supplier prices. Buy flour as "a 1000g bag for €2" instead of having to work out and type a per-gram cost by hand — the exact per-unit cost is derived and stored automatically in the background, and everywhere that cost feeds into (recipe costing, stock value, supplier comparisons) keeps working exactly as before. Existing materials are unaffected — they're treated as a pack of 1, same as today.
  • Fixed Unit Cost silently rounding down to "€0.00" wherever it was shown as a standalone per-unit rate (Materials list, Material details, Recipe cost-estimate breakdowns, Supplier details) — a real cost like €0.002/g was being truncated to 2 decimal places for display. It now shows enough precision to actually be meaningful.
  • Fixed buttons feeling unresponsive or needing a second tap on iPad Safari across the POS Cashier and public storefront — the app never told iOS to skip its default double-tap-to-zoom detection delay, which can eat a fast, deliberate tap. Also enlarged several touch targets that were well under Apple's recommended minimum size for a fingertip: the POS cart's quantity +/- and Remove controls, the Pay/Hold/Clear buttons, and the storefront cart's quantity +/- and floating "Checkout" button (previously as small as ~16px, now 36–48px).
  • Admin → Feedback: added a Priority field (Urgent / High / Medium / Low) alongside Status, editable inline from the same dropdown pattern. Tickets are now sorted by priority within each status group, so the most urgent open items always surface first.
  • Fixed the Data page crashing with "Decimal objects are not supported" right after saving a Material — the new Purchase Quantity field wasn't included in the step that converts database values into plain data before sending them to the page, for both a Material's own record and its per-supplier prices.
  • Dashboard: the fixed sidebar now only shows at true desktop/laptop widths. Every iPad size (Mini, Air, and Pro in portrait, up to 1024px) previously got the full 230px sidebar forced on top of already-tight content — they now get the same collapsible hamburger menu already used on phones, giving the actual page far more room.
  • Fixed the storefront's checkout, cart, and item-detail sheets getting cropped on iPhone Safari with the "Pay" button pushed out of reach — they sized themselves against the full-screen viewport height instead of the actually-visible area above Safari's address bar and toolbar. Also padded each sheet's bottom edge for the home-indicator area on notched iPhones.
2.13.1 — 20 juillet 2026Correctif
  • Fixed every Materials/Products/Recipes/Suppliers/Feedback save showing a generic "Invalid input data" error with no explanation. The server always computed the exact field and reason (e.g. "SKU is required", "Price must be non-negative"), but every form was discarding that detail before showing it. Errors now say exactly which field is wrong and why, and highlight that field in red on the form — instead of leaving you to guess.
  • Fixed saving a Material or Product failing with "Price can only have 2 decimal places" even when the price you typed was a clean whole number. Any account on a display currency other than IDR converts prices through a floating-point exchange rate before saving, which almost never lands on an exact 2-decimal value — that unrounded value was then rejected by the server. Converted prices are now rounded to 2 decimals at the source, so this can no longer happen.
  • Fixed linking a recipe to a Product sometimes leaving Cost Price stuck at a stray "0" instead of the recipe's real cost. A cheap recipe's per-unit cost can be a real, non-zero amount in the base currency (IDR) that still rounds to 0.00 once converted to a stronger display currency — that rounded-to-zero value was being written into the field anyway (now it's left alone instead), and a separate rendering bug was displaying that 0 as a stray floating character on the form even when the field itself was otherwise empty.
  • Data → Products: Cost Price is now locked to the recipe's auto-calculated value whenever a recipe is linked, so it can't drift out of sync with the recipe by accident. A "Customize cost price manually" checkbox (off by default) unlocks the field for a manual override when you actually need one.
2.13.0 — 20 juillet 2026Fonctionnalité
  • Data → Products: linking a recipe to a product now auto-calculates the Cost Price from that recipe's real ingredient cost (summed across every linked recipe if more than one), instead of requiring manual entry. The field stays editable — this is a smart default, not a locked value — and only kicks in when you actively change the recipe selection, so opening an existing product never overwrites a price you already set.
  • Data → Materials and Products: adding a new item now suggests a SKU automatically from the name/category (still fully editable, with a regenerate button), and shows a live "available" / "already used" check as you type — no more finding out about a duplicate SKU only after a failed save. The Category field is now a searchable picker suggesting categories you've already used, instead of free text, to cut down on near-duplicate categories from typos — you can still type a brand-new one.
2.12.0 — 19 juillet 2026Fonctionnalité
  • Admin Feedback console: entries now group by status (Open → In Progress → Resolved → Archived) with newest first within each group, each row/card and its status dropdown are color-coded by status, and every entry shows its ID (click to copy) for referencing a specific ticket.
  • POS orders can now be cancelled — from the Active Queue (a Cancel button on each order card) or from Order History (a Cancel button in the order detail view). Cancelling an order that was already marked Delivered automatically restores the stock that had been deducted for it, so inventory numbers stay accurate.
  • POS Cashier: a new "Hold" button next to Pay lets a cashier park the current cart aside (labeled by customer/table) to serve someone else, without completing payment. Held orders show up immediately in the Active Queue (with a Resume button) and Order History, but stay out of the Kitchen Display until they're resumed and actually paid — and out of every revenue/analytics report until then too. Resuming reloads the cart for editing; paying finalizes the same order rather than creating a duplicate.
  • Fixed the Active Queue order cards' action buttons (Start Processing, Resume, Complete) wrapping into and overlapping the neighboring card, which also cut off the Cancel button.
  • The Enterprise plan's pricing CTA now opens a WhatsApp chat instead of the old Calendly booking link. Every public-facing contact point across the site (Privacy, Press, Careers, GDPR, Partners, Contact, Refund Policy, Terms, footer, checkout-failed screen, and FAQ) now points to the same three real addresses — cro@, ceo@, and consult@prionation.io — and the same real WhatsApp number, replacing several stale placeholder emails and a wrong WhatsApp number.
  • Storefront Settings now has a "Show QR" button next to your storefront link, generating a downloadable QR code for it. Tables Manager can now generate a QR code per table (scanning it opens the menu with that table's number already filled in for ordering) — download one at a time, or all at once as a single labeled sheet for printing table tents.
  • Storefront Settings now has an Opening Hours editor — set open/close times or mark each day closed, shown on your public storefront page. This was previously only possible to display, not edit.
  • Fixed the Tables Manager's per-table QR/Edit/Delete icons being invalid HTML (buttons nested inside another button), which was triggering a React hydration warning.
  • Profile settings now offer a searchable picker for currency (~150 world currencies, up from 4) and timezone (the full worldwide IANA list, up from 5), instead of a short fixed dropdown. Currency conversion now works for any of them — previously only USD actually converted prices (EUR/MGA just relabeled the number); the POS Cashier screens (cart, checkout, order history) previously didn't convert at all and now do, matching what Dashboard finance/materials pages already did.
  • The Admin panel's Region column now shows "Unknown" instead of a specific-looking but potentially fake city/currency for any account whose timezone has never actually been confirmed by a real device sync — previously every account showed "Jakarta · IDR" from the moment it was created, whether or not that was real. Also fixed the existing browser-timezone auto-sync silently never re-confirming itself for a device that had already synced once before this tracking existed.
2.11.0 — 14 juillet 2026Fonctionnalité
  • Installed Google Analytics 4 and the Meta (Facebook) Pixel, both gated behind the cookie-consent bar (GA needs analytics consent, Meta needs marketing consent) and both wired to fire nothing until the visitor actually grants it.
  • Instrumented the full funnel with GA4 events: marketing CTA clicks (hero, header, pricing plans, "book a demo", email capture, contact form), signup/login, every onboarding step plus final completion, dashboard activity (creating a product/material/recipe/supplier/production batch), POS checkout (as a standard e-commerce purchase event with line items), and billing (checkout started, subscription confirmed).
  • Meta Pixel now also fires a CompleteRegistration event on signup, and got two reliability fixes: the pixel ID is hardcoded as a fallback so it still works if the hosting provider's env config is missing it, and the base code renders as a literal <script> tag physically inside <head> (a Next.js quirk meant it wasn't showing up as real markup, which is what Meta's own install check looks for).
2.10.0 — 8 juillet 2026Fonctionnalité
  • POS plan now comes with a 14-day free trial — connect a card, pay nothing for 14 days, then it renews automatically. The POS plan is highlighted as a special promo on the pricing page.
  • Clearer plan boundaries — Online Orders and Table Reservations now require the POS plan; Data (Materials/Recipes/Products/Suppliers) stays an Operations feature. Hitting a feature your plan doesn't include shows an "Upgrade to POS" prompt (or sends you to pricing) instead of failing silently.
  • Every plan now has a proper home for its menu: FREE manages a display-only Store Menu from Storefront settings; POS and up get a dedicated Menu page (same data, editable items with full edit/delete support); Operations' Data page keeps the deeper Product/recipe/stock layer, auto-synced to the menu so a Product's name/price never drifts from what customers and the POS Cashier see.
  • Onboarding can now import your existing menu from a CSV / old data file (POS plan).
  • Admin "Reset Account Data" now also signs the user out on all devices so they cleanly restart from onboarding.
  • Log in and sign up are now one page with a toggle instead of two separate screens — same /login and /register links still work.
  • Menu items can now have a photo — add or edit an item to upload one, with a size/resolution guide (square, 500×500 px ideal, max 2 MB).
  • Fixed a redirect loop that could bounce a freshly-logged-in new user between /login and /stores instead of landing them on onboarding.
  • Fixed rate limiting: every endpoint a user/IP hit shared one counter, so routine background checks (e.g. subscription status) could silently exhaust the much tighter budget for payment-sensitive endpoints like checkout, wrongly blocking the very first attempt. Each endpoint now has its own independent counter.
  • Fixed stock/quantity fields (stock adjustment, materials, products, recipes, supplier orders, production batches) rejecting small decimals like 0,02 or 0,002 — inputs now accept both comma and period as the decimal separator, and precision was widened to 3 decimal places end-to-end (matching what the stock-adjustment ledger already supported) so gram/millilitre-level measurements track correctly. Also fixed the same comma-decimal typing bug on every price/cash field (cost price, selling price, supplier price, menu item price, POS cash tendered, shift opening/closing cash), and a follow-up bug where a mid-typing value could get rewritten (e.g. "0,0" collapsing back to "0").
  • Material/product/recipe/supplier picker dropdowns (stock adjustment, recipe ingredients, supplier reordering, delivery receiving, production) are now sorted alphabetically instead of by creation date, so they're easier to scan.
  • Fixed recipe cost calculations being off by up to 1000x whenever an ingredient's unit (e.g. grams) differed from its material's stock unit (e.g. kilograms) — cost per batch, ingredient cost breakdowns, production batch cost analysis, and stock deduction on sale now correctly convert between units before multiplying. The recipe editor's ingredient unit field is now locked to the material's unit to prevent the mismatch from being reintroduced.
  • Fixed a crash opening any supplier with a phone number saved in a spaced-out format (e.g. "+33 3 88 45 12 67") instead of strict E.164 — the phone input now normalizes on read, and new/edited supplier phone numbers are validated up front.
  • Products are now automatically added to the store's POS/storefront menu as soon as they're created (via the Data page or CSV import), grouped under a menu category matching the product's own category — no more manually clicking "Add to POS menu" for every item. You can still remove any item from the menu manually at any time. Existing products that weren't yet in the menu have been added.
  • Increased the max zoom on the Instagram onboarding profile-picture cropper from 300% to 700%, so a small or low-res profile photo can still be framed to fill the crop area.
2.9.0 — 7 juillet 2026Fonctionnalité
  • Instagram quick-start onboarding — upload a screenshot of your Instagram profile and AI pre-fills your storefront: business name, tagline from your bio, storefront URL from your username, Instagram link, a brand color, and smarter menu suggestions. Crop your profile picture straight from the screenshot to use as your logo — or skip and set up manually as before.
2.8.0 — 5 juillet 2026Fonctionnalité
  • Customer feedback widget — report a bug or suggest a feature from the dashboard, with an optional screenshot; tickets are emailed to the team and users get a "My tickets" tab to track, edit, or delete their own submissions.
  • Admin feedback console — review all tickets with status control (Open / In Progress / Resolved / Archived), an in-place screenshot preview, and a mobile-friendly layout.
  • Order history — the POS Orders page now has "Active" and "History" tabs with search, status/source/date filters, an order-detail dialog, and Excel export.
  • Customer "My orders" — storefront visitors can see the orders they placed on this device, with live status, from a new My Orders page.
  • Dashboard analytics — a reporting section with Revenue, Orders, Average Order Value, and Customers KPIs, revenue-trend and orders-by-status charts, a date range, and Excel export.
  • Mobile-first pass — the POS cashier cart now works on phones (floating bar + bottom sheet), dense tables collapse to cards on small screens, and pinch-zoom is disabled app-wide for a native feel.
  • Install app — a cross-platform "Install app" button in the mobile menu that also guides iOS Safari users through installation.
  • Changelog & versioning — this changelog is now database-backed, surfaced through a "What's new" notification in the dashboard bell and a clickable version badge in the footer and dashboard.
2.7.0 — 30 juin 2026UX
  • Public storefront is now fully light & dark theme-aware — fixed washed-out, unreadable text in dark mode across the menu, item detail, cart, checkout, and order-status pages by moving to semantic design tokens.
  • Storefront language switcher (Indonesian / English / French) plus a light/dark toggle added to every storefront page; the visitor's browser language is auto-detected on first visit.
  • Storefront fully internationalized across id / en / fr — menu, cart, checkout drawer, item detail, order status, and store profile.
  • Storefront profile footer redesigned — compact, with the Epidom logo + wordmark, and trimmed empty space.
  • Home page: smoother section transitions — removed the stray dark band that appeared between adjacent warm (cream) sections.
  • Fixed a React hydration error on the storefront (the open/closed badge and theme toggle now resolve on the client).
  • Fixed dialog action buttons touching with no gap (Add Menu Item and POS checkout).
  • Fixed missing translations in the storefront menu editor's Add Item dialog.
  • Admin: Revenue report now reads live Stripe data with multi-currency views (IDR / EUR / USD), a per-month cash-collected recap, a customer payment log, and one-click Export / Print to PDF.
2.6.0 — 29 mai 2026UX
  • Storefront logo & cover image are now photo-upload fields — drag-and-drop with preview, auto-compression, and resolution guide (logo 400×400 · cover 1920×1080).
  • Data / Products — 'Add to POS menu' button now shows 'In Menu' badge instantly (optimistic update) instead of waiting for a page reload.
  • Sync-to-menu prompt: editing a product's price or name now offers a one-click action to update the linked POS menu item.
  • Recipe cards show a '47× last 30d' demand badge — pulls 30-day POS order counts so you know which recipes are driven by real sales.
  • Tracking page gains a 'Recent Movements' tab showing all stock changes with source context (POS order # / Batch # / Manual).
  • Dashboard now includes a 'Recent Stock Movements' card as a live activity feed.
  • Subscription pricing on profile page updates instantly when you change your currency (IDR / USD / EUR) — no reload needed.
  • Pricing labels corrected: POS = Rp 99.000/bln, OPERATIONS = Rp 249.000/bln.
  • PWA install button in the topbar — disappears automatically when the app is already installed.
  • Removed orphaned placeholder component (data-manage.tsx).
2.5.0 — 28 mai 2026Infra
  • Prisma 6 → 7 — migrated to pg driver adapter (@prisma/adapter-pg); removed url/directUrl from schema.prisma; added prisma.config.ts for CLI.
  • Added DIRECT_URL support (Neon non-pooled endpoint) so prisma migrate deploy runs over a direct connection and never hits pgBouncer prepared-statement limits.
  • build script: prisma migrate deploy now runs before next build — missing columns (isAdmin, hasOnboarded) are created automatically on every Vercel deploy.
  • Service worker fix: response.clone() was called inside an async .then(), causing 'body already used' errors that blocked login. Fixed to clone synchronously.
  • OAuth errors now redirect to /login?error=<code> with a human-readable toast instead of Better Auth's raw HTML error page.
2.4.0 — 28 mai 2026Fonctionnalité
  • Guided onboarding (5 steps: business → logo → menu → theme → publish) with server-side redirect guard after completion (hasOnboarded flag).
  • POST /api/onboarding/complete — marks onboarding done permanently; subsequent visits to /onboarding redirect to /stores instantly.
  • Fixed onboarding menu-item save bug: createCategory() was returning the inner payload directly but code was reading .data?.id — items silently skipped. Fixed.
  • Staff email invitation flow — PIN delivered via Resend after invite.
  • Table reservations — per-table toggle, public booking form, dashboard management panel.
  • Notification bell with badge count for unread alerts.
2.3.0 — 24 mai 2026UX
  • Full i18n sweep — 100+ new translation keys across POS, KDS, Tables, Storefront editor, Finance pages.
  • epi-navy dark / epi-cream light design tokens fully bridged into shadcn CSS variables.
  • Dark/light mode toggle in topbar; default dark.
  • Currency provider — formatPrice() auto-converts from stored value to user's selected currency (IDR / USD / EUR).
  • Shift management — currency-aware formatting, sortable columns.
  • Account Settings — data usage stats, linked accounts, change password, delete account.
2.2.0 — 23 mai 2026Fonctionnalité
  • Finance reports — daily/weekly/monthly P&L: revenue, COGS, gross margin. Per-channel breakdown (DIRECT / GoFood / GrabFood / ShopeeFood / Tokopedia).
  • Multi-outlet owner dashboard (ENTERPRISE) — rolls up all stores with drill-down.
  • Aggregator email ingestion — GoFood/GrabFood order emails → Order records via Inngest + OpenAI.
  • Automatic stock deduction on order → DELIVERED (serializable transaction through Recipe → Material chain).
  • LOWSTOCK and CRITICALSTOCK alerts with notification bell badge.
  • Staff PIN login — clock-in with PIN, shift open/close.
2.1.0 — 22 mai 2026Fonctionnalité
  • POS cashier — menu grid, cart, checkout dialog (CASH / CARD / TRANSFER / QRIS).
  • Kitchen Display System (KDS) — real-time order columns by status.
  • Order queue with SSE real-time updates.
  • Table management — seat assignment, status tracking (FREE / OCCUPIED / RESERVED).
  • Production batch management — schedule batches from recipes, track status, view history.
  • CSV Smart Import (AI-powered) for Products, Materials, Recipes, Suppliers.
2.0.0 — 21 mai 2026Fonctionnalité
  • Public storefront at /@slug — customizable menu page, theme color, tagline, social links.
  • Online ordering — customer checkout form, QRIS/cash payment, order tracking page.
  • Storefront editor — WYSIWYG menu builder with drag-and-drop category reordering.
  • GoFood / GrabFood / ShopeeFood / Tokopedia aggregator link fields.
  • Storefront analytics — view counts.
  • Inventory management — Products, Materials, Recipes (with cost-per-batch), Suppliers.
1.0.0 — 1 mai 2026Fonctionnalité
  • Public launch — auth (email/password + Google OAuth), store creation, billing (FREE / POS / OPERATIONS / ENTERPRISE via Stripe).
  • Dashboard with stock overview, production chart, alerts card, supplier card.
  • Better Auth integration — HMAC-signed cookies, email verification, password reset.
  • Multi-store support (Business → Stores hierarchy).
  • Indonesian (id) primary language, English (en) secondary.