Notifications
Catalog synced from Google Sheets — last updated 12 minutes ago.

All Discounted Products

By M.Srour

Add / edit one product

SKU is the main ID everything else (offers, imports) maps to — it's separate from the barcode. Editing an existing SKU here updates it everywhere.

Bulk upload master data

Upload a .csv or .xlsx. Columns: SKU, Name, NameAR, Brand, Category, Barcode, ProductLink, Description. Existing SKUs get updated, new ones get added — this is how you avoid re-typing brand/category/barcode for every offer.
Click to upload a .csv or .xlsx file
0 SKUs currently in the master list.

Sync from Google Sheets

Same columns as the CSV upload. Auto-syncs every 5 minutes once a URL is entered here (share the sheet as "Anyone with the link → Viewer" first).
Last synced never.

Product Master list

SKUName (EN)Name (AR)BrandCategoryBarcodeLink

1 Add manually

Pick a SKU from Product Master — name, brand, and category fill in automatically, so only the offer details need entering.
Flash shows automatically once an offer runs 7 days or less with over 40% off — the checkbox above is for flagging any other offer as Flash by hand too.

2 Upload Excel / CSV

Upload a .csv or .xlsx. Columns: SKU, Promo Type, Promo Value, Price, Old Price, Start Date, End Date, Channel, FlashManual (Y/N), MMFamily (free/percent/fixedprice/bundle — Mix & Match only), MMBuy, MMValue, DescriptionArOverride (optional custom Arabic phrase — leave blank to auto-generate). Name/brand/category aren't needed here — they're pulled from Product Master by SKU. The sample below has one example row per offer type and per attribute combination (both channels, online-only, flash on/off, missing old price, custom override, etc.) — check a real upload against it column-by-column before trusting a big batch.
Click to upload a .csv or .xlsx file
Export the current catalog to hand off or archive:

3 Sync from Google Sheets

Connect a shared sheet as the live source of truth (share it as "Anyone with the link → Viewer"). This auto-syncs every 5 minutes once a URL is entered here, plus you can always hit Sync now for an immediate pull. Note: this reads the sheet into the portal — a real instant "the moment I edit the sheet" push isn't possible from a page with no backend, so 5-minute polling is the closest safe equivalent.
Not connected yet.

4 Inventory — stock per store

Powers "Only items in stock at my store" on the staff Print All screen. Sheet columns: SKU, Store Number, Quantity — Store Number must match the "Store Number" field on each store in Manage Stores.
Not connected yet.
These three input methods all write to the same product list — an offer added manually, imported from CSV, or synced from Sheets shows up identically to staff, including in every section below.

Delete sample data

Wipes all test offers, the product catalog, notifications, and Bupa test records (Bupa only if you check the box below) — before you start loading real data. Never touches your real stores or user accounts. Cannot be undone.
Editing template:

Wording & variables

All static text prints in Arabic (thermal is monochrome, so no color coding — wording and layout carry the distinction instead). Toggles control which variable fields print at all.
The default currency symbol is the official Saudi Riyal sign (Unicode U+20C1, standardized September 2025) — it's placed to the left of the price with a space, per SAMA's rule, regardless of the surrounding Arabic direction. This is a very new character: please do one real test print, since older printer firmware or fonts without an updated glyph set may not render it yet. If it doesn't print correctly on your Zebra, this field is fully editable — swap in whatever your printer supports.
Which phrase applies is decided per-offer by its Buy/Get numbers (set on the offer itself) — not chosen here. A custom Arabic override on any single offer always wins over either phrase.
Changes apply immediately — to every future preview and print, no save button needed.

Field positions & typography

Drag any field to reposition it, drag its right edge to resize it, or click it to edit bold/size. Shown here is the FULL physical label (70mm × 139mm) — the hatched zones above and below are outside the printer's printable window and will never actually print, no matter what's placed there; only the clear band in the middle (8–13.5cm) can hold fields. The ruler ticks are real millimeters.
Zoom:
If you're printing onto blank pre-printed thermal roll stock, upload a real photo of it here — it'll show faded behind the fields below so you can drag each one to line up exactly with the real label, instead of guessing coordinates. This photo is never printed, it's only a visual guide while editing.

Live preview

Rendered with a sample product so you can see the effect of each change right away — including drags and resizes below, so you can edit and check the result side by side without scrolling.

Section order & descriptions

Reorder, rename, or hide any section — changes apply to Staff View immediately.

Customer sharing channels

Controls which options staff see on the "Send to customer" button. WhatsApp opens via whatever WhatsApp account is already logged into on that PC/browser (WhatsApp Web) — there's no separate login inside the portal. SMS opens the device's default messaging app, which generally only works on mobile, not desktop.

Push a notification

Sent to selected stores. Stays visible on staff screens until each staff member presses Acknowledge — this is logged below.

Notification log

Click a row to see exactly who acknowledged and when.
TitleSentTargetAcknowledged

Preview as a staff member

Testing tool for this demo only — lets you see the acknowledge flow from a specific store's point of view. Not shown to staff.

All offers — activate / deactivate

Turning an offer off hides it from staff immediately, without needing to change its dates. Ended offers stay listed here for you to manage even though staff no longer see them anywhere except "Ended This Week."
0 selected
SKUProductBrandTypeChannelEndsStatusFlashFocusActiveEdit

Bupa master data — Google Sheet sync

This sheet only ever holds dispensed approvals — there's no separate list of "not yet dispensed" prescriptions. A number not on this list is what tells staff it's clear to dispense. Reads from this sheet auto-sync every 5 minutes once a URL is entered, plus Sync now for an immediate pull. Every time staff press Dispense, a new record (store, staff name, exact date & time) is added on this side — pushing that back into the actual sheet still needs the Apps Script bridge described below, since a static page can't write to Sheets on its own.
Last synced .
This demo keeps records in the browser and only simulates the write-back — a real deployment needs a small bridge (a Google Apps Script Web App endpoint is the simplest option) since a static page can't authenticate to Google Sheets directly. Happy to wire that up once it's confirmed available.

Dispensed records

Approval No.StoreDispensed ByDate & Time

Add new user

Passwords are hashed (bcrypt) and checked server-side on login — real, not a demo shortcut. Type carefully: this password box is plain text on screen so you can see what you're entering, but nothing is ever stored or transmitted unhashed.

Bulk add users via CSV

Columns: Username, Password, Role (staff/admin), Store (store name — only needed for staff). Existing usernames get updated (password/role/store), new ones get added.
Click to upload a .csv or .xlsx file

Sync from Google Sheets

Same columns as the CSV upload. Auto-syncs every 5 minutes once a URL is entered here (share the sheet as "Anyone with the link → Viewer" first).
Last synced never.

All users

UsernameRoleStorePassword
Checking database connection…

Print report

Labels printed per store, from the day this was turned on. Older prints were never logged and can't be recovered.
Pick a range and click Run.

Add a barcode manually

Link one extra barcode to a SKU — for the case where a scan comes back "not found" and you already know which item it is.

Bulk upload barcodes

Upload a .csv or .xlsx with columns: Barcode, SKU — one row per barcode. An item with several barcodes gets several rows with the same SKU. Existing barcodes get their SKU updated; new ones are added.
Click to upload a .csv or .xlsx file

All barcodes (0)

BarcodeSKUSourceUpdated byUpdated

Add one store

An internal ID gets generated automatically from the name — you never need to think about it. Re-adding a store with the exact same name updates it instead of creating a duplicate.

Bulk add via CSV

Columns: Name, Store Number (optional). Built for adding many stores at once — 150+ works fine.
Click to upload a .csv or .xlsx file

Sync from Google Sheets

Same columns as the CSV upload. Auto-syncs every 5 minutes once a URL is entered here.
Last synced never.

All stores

Store nameStore numberInternal ID

Add / edit a coupon

Re-adding the same code updates it instead of creating a duplicate.

Upload eligible items for a coupon

Only SKUs on this list are considered eligible when staff check a coupon — re-uploading replaces the previous list for that coupon entirely.
Click to upload a .csv or .xlsx file (SKU, Name)

All coupons

CodeDiscountMin orderMax discountValidEligible itemsActive

Add / edit a contest

Staff earn direct commission on items in this list during the contest window — not a discount, a focus/incentive tool.

Upload items for a contest

Re-uploading replaces the previous item list for that contest entirely.
Click to upload a .csv or .xlsx file (SKU, Name, Commission)

All contests

NameDescriptionValidItemsActive

Add / edit a flyer

Staff see a button for each flyer below, but ONLY once it has a link — you can create a named slot ahead of time and paste the link in whenever it's ready.

All flyers

NameLinkOrderActive
By M.Srour