Enter your secret recovery code to reset the staff password. This is set by the owner in Settings.
Enter the owner password to continue.
Loading…
Tap Copy to use any template. Replace [NAME], [ORDER], [DATE] with real values.
Up to 20 lbs/week wash & fold
Up to 40 lbs/week wash & fold
Unlimited lbs/week wash & fold
Save data to see today's summary.
| When | Machine | Amount | Source | Status | Action |
|---|---|---|---|---|---|
| Loading… | |||||
| Machine | Coins Today | $ Today | All-Time Coins | All-Time $ |
|---|---|---|---|---|
| Loading… | ||||
Toggle each module on or off. Enabled modules appear as tabs in the navigation bar above.
Get a notification on this device when a new order comes in. Each device must be enabled separately — tap the button on every phone, tablet, or POS that should receive alerts.
First time? You'll need to: (1) generate a VAPID key in Firebase Console → Cloud Messaging, paste it into FCM_VAPID_KEY in admin.html, (2) confirm the FIREBASE_SERVICE_ACCOUNT env var is set in Netlify. See PUSH-NOTIFICATIONS-RUNBOOK.md for step-by-step.
Set a private recovery code only you know. Use it on the login screen if you ever forget your password.
Enter your Google Apps Script web app URL to automatically sync daily revenue data to Google Sheets. Editing this link requires the owner password.
function doPost(e) {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = JSON.parse(e.postData.contents);
var HEADERS = ['Date',
'Washers ($)','Washer Qtrs','Dryers ($)','Dryer Qtrs','Quarter Total',
'Vending','Wash & Fold','Machine Total',
'ATM Had','ATM Added','ATM Total',
'$20 Bills','$10 Bills','$5 Bills','$1 Bills','Bill Total',
'Last Updated'];
var row = [
data.date,
data.washers || 0,
data.qtrWashers || 0,
data.dryers || 0,
data.qtrDryers || 0,
data.quarterTotal || 0,
data.vending || 0,
data.washfold || 0,
data.machineTotal || 0,
data.atmHad || 0,
data.atmAdded || 0,
data.atmTotal || 0,
data.bills20 || 0,
data.bills10 || 0,
data.bills5 || 0,
data.bills1 || 0,
data.billTotal || 0,
data.savedAt || new Date().toISOString()
];
// Auto-create header row if sheet is empty
if (sheet.getLastRow() === 0) {
sheet.appendRow(HEADERS);
var hRange = sheet.getRange(1, 1, 1, HEADERS.length);
hRange.setFontWeight('bold');
hRange.setBackground('#7c3aed');
hRange.setFontColor('#ffffff');
}
// Find existing row for this date (so re-syncing updates instead of duplicating).
// Column A may contain either a Date object (Sheets auto-parsed it) or a string,
// so normalize both sides to YYYY-MM-DD before comparing.
function normalizeDate_(v) {
if (v instanceof Date) {
var tz = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone() || 'America/New_York';
return Utilities.formatDate(v, tz, 'yyyy-MM-dd');
}
return String(v || '').slice(0, 10);
}
var targetDate = normalizeDate_(data.date);
var lastRow = sheet.getLastRow();
var existingRow = -1;
if (lastRow > 1) {
var dates = sheet.getRange(2, 1, lastRow - 1, 1).getValues();
for (var i = 0; i < dates.length; i++) {
if (normalizeDate_(dates[i][0]) === targetDate) { existingRow = i + 2; break; }
}
}
if (existingRow > 0) {
sheet.getRange(existingRow, 1, 1, row.length).setValues([row]);
} else {
sheet.appendRow(row);
// Force the new date cell to plain text so Sheets won't auto-parse
// it into a Date object (which causes timezone-shift mismatches on
// subsequent upserts and creates duplicate rows).
var newRow = sheet.getLastRow();
sheet.getRange(newRow, 1).setNumberFormat('@');
}
// Auto-resize columns for readability
sheet.autoResizeColumns(1, HEADERS.length);
return ContentService
.createTextOutput(JSON.stringify({status:'ok'}))
.setMimeType(ContentService.MimeType.JSON);
} catch(err) {
return ContentService
.createTextOutput(JSON.stringify({status:'error', message:err.toString()}))
.setMimeType(ContentService.MimeType.JSON);
}
}
Step 4. Click Deploy → New Deployment.Export all orders to CSV, or clear all stored data.
Share this link with your customer to collect payment via Stripe.
Search to select existing, or type a new name below.
This will remove all items and the current customer.