From 14958ca8518a14d286afd753e06b32d4ff629071 Mon Sep 17 00:00:00 2001 From: odzugkoev Date: Thu, 23 Oct 2025 19:02:56 -0400 Subject: [PATCH] Initial commit --- .dockerignore | 15 + .env | 15 + .gitignore | 37 + Dockerfile | 54 + api-client.js | 94 + config.js | 159 ++ database.js | 721 +++++++ ecosystem.config.js | 17 + package.json | 32 + print-worker.js | 157 ++ printer.js | 1757 +++++++++++++++++ public/css/style.css | 1604 +++++++++++++++ public/images/.gitkeep | 2 + public/js/common.js | 59 + public/js/dashboard.js | 953 +++++++++ public/js/settings.js | 709 +++++++ public/sounds/canceled-order-notification.mp3 | Bin 0 -> 33024 bytes public/sounds/new-order-notification.mp3 | Bin 0 -> 138762 bytes public/uploads/.gitkeep | 2 + .../uploads/canceled-order-notification.mp3 | Bin 0 -> 33024 bytes public/uploads/new-order-notification.mp3 | Bin 0 -> 138762 bytes routes/auth.js | 117 ++ routes/dashboard.js | 35 + routes/orders.js | 343 ++++ routes/settings.js | 459 +++++ server.js | 442 +++++ test-startup.js | 128 ++ test-utils.js | 184 ++ updater.js | 94 + views/dashboard.ejs | 145 ++ views/login.ejs | 80 + views/settings.ejs | 523 +++++ 32 files changed, 8937 insertions(+) create mode 100644 .dockerignore create mode 100644 .env create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 api-client.js create mode 100644 config.js create mode 100644 database.js create mode 100644 ecosystem.config.js create mode 100644 package.json create mode 100644 print-worker.js create mode 100644 printer.js create mode 100644 public/css/style.css create mode 100644 public/images/.gitkeep create mode 100644 public/js/common.js create mode 100644 public/js/dashboard.js create mode 100644 public/js/settings.js create mode 100644 public/sounds/canceled-order-notification.mp3 create mode 100644 public/sounds/new-order-notification.mp3 create mode 100644 public/uploads/.gitkeep create mode 100644 public/uploads/canceled-order-notification.mp3 create mode 100644 public/uploads/new-order-notification.mp3 create mode 100644 routes/auth.js create mode 100644 routes/dashboard.js create mode 100644 routes/orders.js create mode 100644 routes/settings.js create mode 100644 server.js create mode 100644 test-startup.js create mode 100644 test-utils.js create mode 100644 updater.js create mode 100644 views/dashboard.ejs create mode 100644 views/login.ejs create mode 100644 views/settings.ejs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8ec290e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +node_modules +npm-debug.log* +.DS_Store +.git +.gitignore +**/*.log + +# Local databases and generated data +/data +/public/uploads + +# Editor/OS +.vscode +.idea +*.swp diff --git a/.env b/.env new file mode 100644 index 0000000..73d6bc6 --- /dev/null +++ b/.env @@ -0,0 +1,15 @@ +# Kitchen Agent Configuration + +# Google reCAPTCHA Site Key (v2 Invisible) +RECAPTCHA_SITE_KEY=6LfoaqIqAAAAABi1P-6T1gQpfXNXMv6aQqH0lwGK + +# Server Configuration +PORT=3000 +HOST=0.0.0.0 + +# Cookie Secret (change this in production!) +COOKIE_SECRET=0e4cfa9b-ba26-43b7-a6a1-a21585a6495b + +# ThinkLink API URL +API_URL=https://api.thinklink.ai + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21cc638 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Dependency directories +node_modules/ +jspm_packages/ # If using JSPM + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Production artifacts +build/ +dist/ +.next/ # For Next.js projects +out/ # For static site generators + +# Editor/IDE specific files +.vscode/ # VS Code settings +.idea/ # IntelliJ IDEA settings +*.sublime-project +*.sublime-workspace + +# OS generated files +.DS_Store +Thumbs.db + +# Test coverage +coverage/ + +# Misc +*.log +*.pid +*.seed +*.gz +pids/ +logs/ +results/ +data/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..936e0b2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +# syntax=docker/dockerfile:1.6 + +FROM node:20-bookworm-slim AS base +WORKDIR /app +ENV NODE_ENV=production + +# Install dependencies first to leverage Docker layer caching +COPY package.json package-lock.json ./ + +FROM base AS build +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + python3 \ + make \ + g++ \ + && rm -rf /var/lib/apt/lists/* + +# Install production deps (will compile native modules like better-sqlite3/serialport if needed) +RUN npm ci --omit=dev + +# Copy source +COPY . . + +FROM node:20-bookworm-slim AS runtime +WORKDIR /app +ENV NODE_ENV=production + +# Runtime OS packages for printing via pdf-to-printer (lp/lpr) +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + cups-client \ + cups-bsd \ + libcups2 \ + fonts-dejavu-core \ + && rm -rf /var/lib/apt/lists/* + +# Copy built app with node_modules from the build stage +COPY --from=build /app /app + +# Create volumes for persistent data and user uploads +VOLUME ["/app/data", "/app/public/uploads"] + +# Ensure non-root runtime; change ownership so the node user can write to volumes +RUN chown -R node:node /app +USER node + +EXPOSE 3000 + +HEALTHCHECK --interval=30s --start-period=30s --timeout=5s --retries=3 \ + CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3000)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" + +CMD ["npm", "start"] + + diff --git a/api-client.js b/api-client.js new file mode 100644 index 0000000..8cd499c --- /dev/null +++ b/api-client.js @@ -0,0 +1,94 @@ +const fetch = require('node-fetch'); + +class APIClient { + constructor(baseUrl = process.env.API_URL || 'https://api.thinklink.ai') { + this.baseUrl = baseUrl; + } + + async request(endpoint, options = {}) { + const url = `${this.baseUrl}${endpoint}`; + + try { + const response = await fetch(url, { + method: options.method || 'POST', + headers: { + 'Content-Type': 'application/json', + ...(options.headers || {}) + }, + body: options.body ? JSON.stringify(options.body) : undefined + }); + + const data = await response.json(); + return data; + } catch (error) { + console.error(`API request failed: ${endpoint}`, error.message); + return { + error: true, + message: `Network error: ${error.message}` + }; + } + } + + async login(email, password, recaptchaToken) { + return this.request('/user/login', { + body: { + login: email, + password: password, + 'g-recaptcha-response': recaptchaToken + } + }); + } + + async getBots(token) { + return this.request('/bot/list', { + body: { + token: token + } + }); + } + + async getOrders(token, botId, afterId = 0, options = {}) { + const body = { + token: token, + botId: parseInt(botId, 10), + afterId: afterId || 0, + limit: options.limit || 50, + includeCanceled: options.includeCanceled || false + }; + + if (options.orderStatus) { + body.orderStatus = options.orderStatus; + } + + if (options.sinceTs) { + body.sinceTs = options.sinceTs; + } + + return this.request('/food-order/orders', { body }); + } + + async modifyOrder(token, botId, orderId, action, cancellationReason = '') { + const body = { + token: token, + botId: parseInt(botId, 10), + orderId: parseInt(orderId, 10), + action: action + }; + + if (action === 'cancel' && cancellationReason) { + body.cancellationReason = cancellationReason; + } + + return this.request('/food-order/modify', { body }); + } + + isTokenExpired(expirationDate) { + if (!expirationDate) return true; + const expiry = new Date(expirationDate); + const now = new Date(); + return now >= expiry; + } +} + +module.exports = new APIClient(); + diff --git a/config.js b/config.js new file mode 100644 index 0000000..0a5eeaa --- /dev/null +++ b/config.js @@ -0,0 +1,159 @@ +const database = require('./database'); +const crypto = require('crypto'); + +class ConfigManager { + constructor() { + this.encryptionKey = null; + } + + getOrCreateEncryptionKey() { + // Lazy initialization - only create key when first needed + if (this.encryptionKey) { + return this.encryptionKey; + } + + // In production, this should be stored securely (environment variable or secure file) + // For now, we'll generate a random key and store it in the database + try { + let key = database.getConfig('_encryption_key'); + if (!key) { + key = crypto.randomBytes(32).toString('hex'); + database.setConfig('_encryption_key', key); + } + this.encryptionKey = key; + return key; + } catch (error) { + // If database not ready, generate temporary key + console.warn('Database not ready, using temporary encryption key'); + this.encryptionKey = crypto.randomBytes(32).toString('hex'); + return this.encryptionKey; + } + } + + encrypt(text) { + if (!text) return null; + try { + const key = this.getOrCreateEncryptionKey(); + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv( + 'aes-256-cbc', + Buffer.from(key, 'hex'), + iv + ); + let encrypted = cipher.update(text, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + return iv.toString('hex') + ':' + encrypted; + } catch (error) { + console.error('Encryption error:', error.message); + return null; + } + } + + decrypt(text) { + if (!text) return null; + try { + const key = this.getOrCreateEncryptionKey(); + const parts = text.split(':'); + const iv = Buffer.from(parts[0], 'hex'); + const encrypted = parts[1]; + const decipher = crypto.createDecipheriv( + 'aes-256-cbc', + Buffer.from(key, 'hex'), + iv + ); + let decrypted = decipher.update(encrypted, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } catch (error) { + console.error('Decryption error:', error.message); + return null; + } + } + + get(key) { + try { + const value = database.getConfig(key); + + // Decrypt token if it's the auth token + if (key === 'authToken' && value) { + return this.decrypt(value); + } + + return value; + } catch (error) { + console.warn(`Config.get('${key}') failed:`, error.message); + return null; + } + } + + set(key, value) { + try { + // Encrypt token if it's the auth token + if (key === 'authToken' && value) { + value = this.encrypt(value); + } + + database.setConfig(key, value); + } catch (error) { + console.error(`Config.set('${key}') failed:`, error.message); + } + } + + getAll() { + try { + const config = database.getConfig(); + + // Decrypt auth token if present + if (config.authToken) { + config.authToken = this.decrypt(config.authToken); + } + + return config; + } catch (error) { + console.warn('Config.getAll() failed:', error.message); + return {}; + } + } + + setMultiple(configObj) { + try { + // Encrypt auth token if present + if (configObj.authToken) { + configObj.authToken = this.encrypt(configObj.authToken); + } + + database.setConfigMultiple(configObj); + } catch (error) { + console.error('Config.setMultiple() failed:', error.message); + } + } + + isAuthenticated() { + try { + const token = this.get('authToken'); + const expiry = this.get('tokenExpiry'); + + if (!token) return false; + if (!expiry) return false; + + const expiryDate = new Date(expiry); + const now = new Date(); + + return now < expiryDate; + } catch (error) { + return false; + } + } + + clearAuth() { + try { + this.set('authToken', ''); + this.set('tokenExpiry', ''); + } catch (error) { + console.error('Failed to clear auth:', error.message); + } + } +} + +module.exports = new ConfigManager(); + diff --git a/database.js b/database.js new file mode 100644 index 0000000..63af416 --- /dev/null +++ b/database.js @@ -0,0 +1,721 @@ +const Database = require('better-sqlite3'); +const path = require('path'); +const fs = require('fs'); + +class DatabaseManager { + constructor() { + this.db = null; + } + + init() { + // Ensure data directory exists + const dataDir = path.join(__dirname, 'data'); + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + } + + const dbPath = path.join(dataDir, 'kitchen.db'); + this.db = new Database(dbPath); + this.db.pragma('journal_mode = WAL'); + + this.createTables(); + console.log('Database initialized successfully'); + } + + createTables() { + // Config table + this.db.exec(` + CREATE TABLE IF NOT EXISTS config ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key TEXT UNIQUE NOT NULL, + value TEXT + ) + `); + + // Orders table + this.db.exec(` + CREATE TABLE IF NOT EXISTS orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + order_id INTEGER UNIQUE NOT NULL, + bot_id INTEGER NOT NULL, + status TEXT NOT NULL, + local_status TEXT, + order_data TEXT NOT NULL, + customer_data TEXT NOT NULL, + total_amount REAL, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL, + printed_at INTEGER, + synced_at INTEGER + ) + `); + + // Print queue table + this.db.exec(` + CREATE TABLE IF NOT EXISTS print_queue ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + order_id INTEGER NOT NULL, + print_type TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'pending', + created_at INTEGER NOT NULL, + printed_at INTEGER, + FOREIGN KEY (order_id) REFERENCES orders(order_id) + ) + `); + + // Printers table - stores individual printer configurations + this.db.exec(` + CREATE TABLE IF NOT EXISTS printers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type TEXT NOT NULL, + interface TEXT NOT NULL, + printer_type TEXT NOT NULL DEFAULT 'epson', + paper_width INTEGER NOT NULL DEFAULT 48, + paper_format TEXT NOT NULL DEFAULT '80mm', + is_default INTEGER NOT NULL DEFAULT 0, + is_enabled INTEGER NOT NULL DEFAULT 1, + font_size TEXT DEFAULT 'normal', + line_style TEXT DEFAULT 'single', + qr_code_enabled INTEGER DEFAULT 1, + qr_code_size INTEGER DEFAULT 3, + qr_code_correction TEXT DEFAULT 'M', + qr_code_content_template TEXT DEFAULT 'ORDER-{id}', + header_text TEXT DEFAULT 'KITCHEN ORDER', + footer_text TEXT DEFAULT 'Thank you!', + business_name TEXT, + business_address TEXT, + business_phone TEXT, + business_website TEXT, + business_email TEXT, + business_contact_size TEXT DEFAULT 'normal', + show_customer_info INTEGER DEFAULT 1, + show_order_items INTEGER DEFAULT 1, + show_prices INTEGER DEFAULT 1, + show_timestamps INTEGER DEFAULT 1, + logo_path TEXT, + logo_max_width_dots INTEGER, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + + // Create indexes + this.db.exec(` + CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status); + CREATE INDEX IF NOT EXISTS idx_orders_local_status ON orders(local_status); + CREATE INDEX IF NOT EXISTS idx_orders_created ON orders(created_at); + CREATE INDEX IF NOT EXISTS idx_print_queue_status ON print_queue(status); + CREATE INDEX IF NOT EXISTS idx_printers_enabled ON printers(is_enabled); + CREATE INDEX IF NOT EXISTS idx_printers_default ON printers(is_default); + `); + + // Initialize default config values if not exists + this.setConfigDefaults(); + + // Migrate old printer config to new table if needed + this.migrateOldPrinterConfig(); + } + + setConfigDefaults() { + const defaults = { + apiUrl: 'https://api.thinklink.ai', + pollingInterval: '15000', + dashboardRefreshInterval: '10000', + printerType: 'epson', + printerInterface: 'usb', + printerPath: '/dev/usb/lp0', + printerWidth: '48', + fontSize: 'normal', + qrCodeEnabled: 'true', + headerText: 'KITCHEN ORDER', + footerText: 'Thank you!', + showOrderStats: 'true', + lineStyle: 'single', + printMargins: JSON.stringify({ left: 0, right: 0 }), + showCustomerInfo: 'true', + showOrderItems: 'true', + showPrices: 'true', + showTimestamps: 'true' + }; + + const insert = this.db.prepare('INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)'); + for (const [key, value] of Object.entries(defaults)) { + insert.run(key, value); + } + } + + // Config operations + getConfig(key = null) { + if (key) { + const row = this.db.prepare('SELECT value FROM config WHERE key = ?').get(key); + return row ? row.value : null; + } + + const rows = this.db.prepare('SELECT key, value FROM config').all(); + const config = {}; + for (const row of rows) { + config[row.key] = row.value; + } + return config; + } + + setConfig(key, value) { + this.db.prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)').run(key, value); + } + + setConfigMultiple(configObj) { + const insert = this.db.prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)'); + const transaction = this.db.transaction(() => { + for (const [key, value] of Object.entries(configObj)) { + insert.run(key, value); + } + }); + transaction(); + } + + // Order operations + insertOrder(order) { + const now = Math.floor(Date.now() / 1000); + return this.db.prepare(` + INSERT INTO orders (order_id, bot_id, status, local_status, order_data, customer_data, total_amount, created_at, updated_at, synced_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `).run( + order.id, + order.botId, + order.status, + 'new', + JSON.stringify(order.order), + JSON.stringify(order.customer), + order.totalAmount, + order.createdAt, + order.updatedAt, + now + ); + } + + getOrderById(orderId) { + const row = this.db.prepare(` + SELECT * FROM orders WHERE order_id = ? + `).get(orderId); + + if (!row) return null; + + return { + id: row.order_id, + botId: row.bot_id, + status: row.status, + localStatus: row.local_status, + order: JSON.parse(row.order_data), + customer: JSON.parse(row.customer_data), + totalAmount: row.total_amount, + createdAt: row.created_at, + updatedAt: row.updated_at, + printedAt: row.printed_at, + syncedAt: row.synced_at + }; + } + + getLastOrder() { + const row = this.db.prepare(` + SELECT * FROM orders ORDER BY order_id DESC LIMIT 1 + `).get(); + + if (!row) return null; + + return { + order_id: row.order_id, + botId: row.bot_id, + status: row.status, + localStatus: row.local_status + }; + } + + updateOrder(order) { + const now = Math.floor(Date.now() / 1000); + return this.db.prepare(` + UPDATE orders + SET status = ?, order_data = ?, customer_data = ?, total_amount = ?, updated_at = ?, synced_at = ? + WHERE order_id = ? + `).run( + order.status, + JSON.stringify(order.order), + JSON.stringify(order.customer), + order.totalAmount, + order.updatedAt, + now, + order.id + ); + } + + updateOrderStatus(orderId, localStatus) { + const now = Math.floor(Date.now() / 1000); + return this.db.prepare(` + UPDATE orders SET local_status = ?, updated_at = ? WHERE order_id = ? + `).run(localStatus, now, orderId); + } + + markOrderPrinted(orderId) { + const now = Math.floor(Date.now() / 1000); + return this.db.prepare(` + UPDATE orders SET printed_at = ? WHERE order_id = ? + `).run(now, orderId); + } + + getOrders(filters = {}) { + let query = 'SELECT * FROM orders WHERE 1=1'; + const params = []; + + if (filters.status) { + query += ' AND local_status = ?'; + params.push(filters.status); + } + + if (filters.date) { + // Get orders from start of day to end of day + const startOfDay = Math.floor(new Date(filters.date).setHours(0, 0, 0, 0) / 1000); + const endOfDay = Math.floor(new Date(filters.date).setHours(23, 59, 59, 999) / 1000); + query += ' AND created_at BETWEEN ? AND ?'; + params.push(startOfDay, endOfDay); + } + + query += ' ORDER BY created_at DESC'; + + if (filters.limit) { + query += ' LIMIT ?'; + params.push(filters.limit); + } + + const rows = this.db.prepare(query).all(...params); + + return rows.map(row => ({ + id: row.order_id, + botId: row.bot_id, + status: row.status, + localStatus: row.local_status, + order: JSON.parse(row.order_data), + customer: JSON.parse(row.customer_data), + totalAmount: row.total_amount, + createdAt: row.created_at, + updatedAt: row.updated_at, + printedAt: row.printed_at, + syncedAt: row.synced_at + })); + } + + getOrderStats() { + const today = Math.floor(new Date().setHours(0, 0, 0, 0) / 1000); + + const stats = { + total: 0, + new: 0, + preparing: 0, + ready: 0 + }; + + const rows = this.db.prepare(` + SELECT local_status, COUNT(*) as count + FROM orders + WHERE created_at >= ? + GROUP BY local_status + `).all(today); + + for (const row of rows) { + if (row.local_status === 'new') stats.new = row.count; + else if (row.local_status === 'preparing') stats.preparing = row.count; + else if (row.local_status === 'ready') stats.ready = row.count; + stats.total += row.count; + } + + return stats; + } + + // Print queue operations + addToPrintQueue(orderId, printType) { + const now = Math.floor(Date.now() / 1000); + const recentWindowSeconds = 300; // 5 minutes window to prevent duplicates + const cutoff = now - recentWindowSeconds; + + // Enhanced idempotency: check for any recent job (pending, processing, or recently completed) + // Apply time window to pending/processing as well to avoid stale blocks + const existing = this.db.prepare(` + SELECT id, status, created_at FROM print_queue + WHERE order_id = ? AND print_type = ? + AND ((status IN ('pending','processing') AND created_at > ?) OR (status = 'completed' AND created_at > ?)) + ORDER BY id DESC LIMIT 1 + `).get(orderId, printType, cutoff, cutoff); + + if (existing && existing.id) { + // If there's a pending or processing job, reuse it + if (existing.status === 'pending' || existing.status === 'processing') { + console.log(`[DB] Reusing existing ${existing.status} job ${existing.id} for order #${orderId} (${printType})`); + return existing.id; + } + // If there's a recently completed job, allow creating a new job for audit + if (existing.status === 'completed') { + console.log(`[DB] Recent completed job ${existing.id} exists for order #${orderId} (${printType}); creating a new queued job.`); + } + } + + console.log(`[DB] Creating new print job for order #${orderId} (${printType})`); + const info = this.db.prepare(` + INSERT INTO print_queue (order_id, print_type, status, created_at) + VALUES (?, ?, 'pending', ?) + `).run(orderId, printType, now); + return info && info.lastInsertRowid ? info.lastInsertRowid : null; + } + + getPendingPrintJobs() { + return this.db.prepare(` + SELECT * FROM print_queue WHERE status = 'pending' ORDER BY created_at ASC + `).all(); + } + + markPrintJobCompleted(id) { + const now = Math.floor(Date.now() / 1000); + return this.db.prepare(` + UPDATE print_queue SET status = 'completed', printed_at = ? WHERE id = ? + `).run(now, id); + } + + markPrintJobFailed(id) { + return this.db.prepare(` + UPDATE print_queue SET status = 'failed' WHERE id = ? + `).run(id); + } + + // Transition a job to processing to prevent the worker from picking it up concurrently + markPrintJobProcessing(id) { + return this.db.prepare(` + UPDATE print_queue SET status = 'processing' WHERE id = ? + `).run(id); + } + + // Revert a job back to pending (e.g., if immediate print failed) so the worker can retry + markPrintJobPending(id) { + return this.db.prepare(` + UPDATE print_queue SET status = 'pending' WHERE id = ? + `).run(id); + } + + hasPrintedCancellation(orderId) { + const row = this.db.prepare(` + SELECT 1 FROM print_queue + WHERE order_id = ? AND print_type = 'canceled' AND status = 'completed' + LIMIT 1 + `).get(orderId); + return !!row; + } + + // Cleanup other jobs for the same order+type when one succeeds + // This prevents duplicate prints from multiple pending jobs + cleanupDuplicateJobs(successfulJobId, orderId, printType) { + const result = this.db.prepare(` + UPDATE print_queue + SET status = 'completed', printed_at = ? + WHERE order_id = ? AND print_type = ? + AND id != ? + AND status IN ('pending', 'processing') + `).run(Math.floor(Date.now() / 1000), orderId, printType, successfulJobId); + + if (result.changes > 0) { + console.log(`[DB] Cleaned up ${result.changes} duplicate job(s) for order #${orderId} (${printType})`); + } + return result.changes; + } + + // Check if there's any active or recently completed job for order+type + hasActiveOrRecentJob(orderId, printType, windowSeconds = 60) { + const cutoff = Math.floor(Date.now() / 1000) - windowSeconds; + const row = this.db.prepare(` + SELECT id, status FROM print_queue + WHERE order_id = ? AND print_type = ? + AND ((status IN ('processing', 'pending') AND created_at > ?) OR (status = 'completed' AND printed_at > ?)) + ORDER BY id DESC LIMIT 1 + `).get(orderId, printType, cutoff, cutoff); + return row ? { hasActive: true, jobId: row.id, status: row.status } : { hasActive: false }; + } + + // Reset stale 'processing' jobs to 'pending' so the worker can pick them up + // Useful when immediate prints failed (e.g., printer was offline) and jobs got stuck + resetStuckProcessingJobs(maxAgeSeconds = 120) { + const cutoff = Math.floor(Date.now() / 1000) - maxAgeSeconds; + const result = this.db.prepare(` + UPDATE print_queue + SET status = 'pending' + WHERE status = 'processing' AND created_at < ? + `).run(cutoff); + if (result && result.changes > 0) { + console.log(`[DB] Recovered ${result.changes} stuck processing job(s) to pending`); + } + return result && result.changes ? result.changes : 0; + } + + // Get count of recently completed jobs for debugging + getRecentlyCompletedJobCount(orderId, printType, windowSeconds = 300) { + const cutoff = Math.floor(Date.now() / 1000) - windowSeconds; + const row = this.db.prepare(` + SELECT COUNT(*) as count FROM print_queue + WHERE order_id = ? AND print_type = ? + AND status = 'completed' + AND printed_at > ? + `).get(orderId, printType, cutoff); + return row ? row.count : 0; + } + + // Printer CRUD operations + addPrinter(config) { + const now = Math.floor(Date.now() / 1000); + const stmt = this.db.prepare(` + INSERT INTO printers ( + name, type, interface, printer_type, paper_width, paper_format, + is_default, is_enabled, font_size, line_style, + qr_code_enabled, qr_code_size, qr_code_correction, qr_code_content_template, + header_text, footer_text, + business_name, business_address, business_phone, business_website, business_email, + business_contact_size, show_customer_info, show_order_items, show_prices, show_timestamps, + logo_path, logo_max_width_dots, created_at, updated_at + ) VALUES ( + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ) + `); + + const info = stmt.run( + config.name || 'Unnamed Printer', + config.type || 'usb', + config.interface || '', + config.printer_type || 'epson', + config.paper_width || 48, + config.paper_format || '80mm', + config.is_default ? 1 : 0, + config.is_enabled !== false ? 1 : 0, + config.font_size || 'normal', + config.line_style || 'single', + config.qr_code_enabled !== false ? 1 : 0, + config.qr_code_size || 3, + config.qr_code_correction || 'M', + config.qr_code_content_template || 'ORDER-{id}', + (typeof config.header_text !== 'undefined' ? config.header_text : 'KITCHEN ORDER'), + (typeof config.footer_text !== 'undefined' ? config.footer_text : 'Thank you!'), + config.business_name || '', + config.business_address || '', + config.business_phone || '', + config.business_website || '', + config.business_email || '', + config.business_contact_size || 'normal', + config.show_customer_info !== false ? 1 : 0, + config.show_order_items !== false ? 1 : 0, + config.show_prices !== false ? 1 : 0, + config.show_timestamps !== false ? 1 : 0, + config.logo_path || null, + config.logo_max_width_dots || null, + now, + now + ); + + // If this is set as default, unset other defaults + if (config.is_default) { + this.db.prepare('UPDATE printers SET is_default = 0 WHERE id != ?').run(info.lastInsertRowid); + } + + return info.lastInsertRowid; + } + + updatePrinter(id, config) { + const now = Math.floor(Date.now() / 1000); + const stmt = this.db.prepare(` + UPDATE printers SET + name = ?, type = ?, interface = ?, printer_type = ?, paper_width = ?, paper_format = ?, + is_default = ?, is_enabled = ?, font_size = ?, line_style = ?, + qr_code_enabled = ?, qr_code_size = ?, qr_code_correction = ?, qr_code_content_template = ?, + header_text = ?, footer_text = ?, + business_name = ?, business_address = ?, business_phone = ?, business_website = ?, business_email = ?, + business_contact_size = ?, show_customer_info = ?, show_order_items = ?, show_prices = ?, show_timestamps = ?, + logo_path = ?, logo_max_width_dots = ?, updated_at = ? + WHERE id = ? + `); + + const result = stmt.run( + config.name || 'Unnamed Printer', + config.type || 'usb', + config.interface || '', + config.printer_type || 'epson', + config.paper_width || 48, + config.paper_format || '80mm', + config.is_default ? 1 : 0, + config.is_enabled !== false ? 1 : 0, + config.font_size || 'normal', + config.line_style || 'single', + config.qr_code_enabled !== false ? 1 : 0, + config.qr_code_size || 3, + config.qr_code_correction || 'M', + config.qr_code_content_template || 'ORDER-{id}', + (typeof config.header_text !== 'undefined' ? config.header_text : 'KITCHEN ORDER'), + (typeof config.footer_text !== 'undefined' ? config.footer_text : 'Thank you!'), + config.business_name || '', + config.business_address || '', + config.business_phone || '', + config.business_website || '', + config.business_email || '', + config.business_contact_size || 'normal', + config.show_customer_info !== false ? 1 : 0, + config.show_order_items !== false ? 1 : 0, + config.show_prices !== false ? 1 : 0, + config.show_timestamps !== false ? 1 : 0, + config.logo_path || null, + config.logo_max_width_dots || null, + now, + id + ); + + // If this is set as default, unset other defaults + if (config.is_default) { + this.db.prepare('UPDATE printers SET is_default = 0 WHERE id != ?').run(id); + } + + return result; + } + + deletePrinter(id) { + return this.db.prepare('DELETE FROM printers WHERE id = ?').run(id); + } + + getPrinter(id) { + const row = this.db.prepare('SELECT * FROM printers WHERE id = ?').get(id); + return row ? this.mapPrinterRow(row) : null; + } + + getAllPrinters() { + const rows = this.db.prepare('SELECT * FROM printers ORDER BY is_default DESC, name ASC').all(); + return rows.map(row => this.mapPrinterRow(row)); + } + + getEnabledPrinters() { + const rows = this.db.prepare('SELECT * FROM printers WHERE is_enabled = 1 ORDER BY is_default DESC, name ASC').all(); + return rows.map(row => this.mapPrinterRow(row)); + } + + getDefaultPrinter() { + const row = this.db.prepare('SELECT * FROM printers WHERE is_default = 1 LIMIT 1').get(); + return row ? this.mapPrinterRow(row) : null; + } + + setDefaultPrinter(id) { + const transaction = this.db.transaction(() => { + this.db.prepare('UPDATE printers SET is_default = 0').run(); + this.db.prepare('UPDATE printers SET is_default = 1 WHERE id = ?').run(id); + }); + transaction(); + } + + togglePrinterEnabled(id) { + const printer = this.getPrinter(id); + if (!printer) return null; + const newEnabled = printer.is_enabled ? 0 : 1; + this.db.prepare('UPDATE printers SET is_enabled = ? WHERE id = ?').run(newEnabled, id); + return { is_enabled: newEnabled === 1 }; + } + + mapPrinterRow(row) { + return { + id: row.id, + name: row.name, + type: row.type, + interface: row.interface, + printer_type: row.printer_type, + paper_width: row.paper_width, + paper_format: row.paper_format, + is_default: row.is_default === 1, + is_enabled: row.is_enabled === 1, + font_size: row.font_size, + line_style: row.line_style, + qr_code_enabled: row.qr_code_enabled === 1, + qr_code_size: row.qr_code_size, + qr_code_correction: row.qr_code_correction, + qr_code_content_template: row.qr_code_content_template, + header_text: row.header_text, + footer_text: row.footer_text, + business_name: row.business_name, + business_address: row.business_address, + business_phone: row.business_phone, + business_website: row.business_website, + business_email: row.business_email, + business_contact_size: row.business_contact_size, + show_customer_info: row.show_customer_info === 1, + show_order_items: row.show_order_items === 1, + show_prices: row.show_prices === 1, + show_timestamps: row.show_timestamps === 1, + logo_path: row.logo_path, + logo_max_width_dots: row.logo_max_width_dots, + created_at: row.created_at, + updated_at: row.updated_at + }; + } + + // Migration from old config system to new printers table + migrateOldPrinterConfig() { + try { + // Check if we already have printers + const existingPrinters = this.db.prepare('SELECT COUNT(*) as count FROM printers').get(); + if (existingPrinters && existingPrinters.count > 0) { + return; // Already migrated + } + + // Get old config + const oldConfig = this.getConfig(); + + // Only migrate if old printer config exists + if (!oldConfig.printerInterface && !oldConfig.printerPath) { + return; // No old config to migrate + } + + console.log('Migrating old printer configuration to new printers table...'); + + // Create a printer from old config + const printerConfig = { + name: 'Default Printer (Migrated)', + type: oldConfig.printerInterface === 'serial' ? 'com' : (oldConfig.printerInterface || 'usb'), + interface: oldConfig.printerPath || '/dev/usb/lp0', + printer_type: oldConfig.printerType || 'epson', + paper_width: parseInt(oldConfig.printerWidth, 10) || 48, + paper_format: (parseInt(oldConfig.printerWidth, 10) || 48) >= 48 ? '80mm' : '58mm', + is_default: true, + is_enabled: true, + font_size: oldConfig.fontSize || 'normal', + line_style: oldConfig.lineStyle || 'single', + qr_code_enabled: oldConfig.qrCodeEnabled !== 'false', + qr_code_size: parseInt(oldConfig.qrCodeSize, 10) || 3, + qr_code_correction: oldConfig.qrCodeCorrection || 'M', + qr_code_content_template: oldConfig.qrCodeContentTemplate || 'ORDER-{id}', + header_text: oldConfig.headerText || 'KITCHEN ORDER', + footer_text: oldConfig.footerText || 'Thank you!', + business_name: oldConfig.businessName || '', + business_address: oldConfig.businessAddress || '', + business_phone: oldConfig.businessPhone || '', + business_website: oldConfig.businessWebsite || '', + business_email: oldConfig.businessEmail || '', + business_contact_size: oldConfig.businessContactSize || 'normal', + show_customer_info: oldConfig.showCustomerInfo !== 'false', + show_order_items: oldConfig.showOrderItems !== 'false', + show_prices: oldConfig.showPrices !== 'false', + show_timestamps: oldConfig.showTimestamps !== 'false', + logo_path: oldConfig.logoPath || null, + logo_max_width_dots: oldConfig.logoMaxWidthDots ? parseInt(oldConfig.logoMaxWidthDots, 10) : null + }; + + this.addPrinter(printerConfig); + console.log('Migration complete: Old printer configuration transferred to new system'); + } catch (error) { + console.error('Error migrating old printer config:', error.message); + } + } + + close() { + if (this.db) { + this.db.close(); + } + } +} + +module.exports = new DatabaseManager(); + diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..826f0b0 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,17 @@ +module.exports = { + apps: [{ + name: 'kitchen-agent', + script: './server.js', + instances: 1, + exec_mode: 'cluster', + kill_timeout: 8000, + listen_timeout: 8000, + env: { + REPO_URL: 'https://repo.cloud.thinklink.ai/thinklink/kitchen-agent', + REPO_BRANCH: 'main', + PM2_APP: 'kitchen-agent', + UPDATE_CHECK_INTERVAL_MS: 300000 + // GITEA_TOKEN: '' + } + }] +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..5c437f3 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "kitchen-agent", + "version": "1.0.5", + "description": "Kitchen Agent for ThinkLink Food Order Management", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "node --watch server.js", + "test": "node run-tests.js" + }, + "keywords": ["kitchen", "food-order", "thermal-printer", "thinklink"], + "author": "", + "license": "MIT", + "dependencies": { + "fastify": "^4.26.0", + "@fastify/view": "^8.2.0", + "@fastify/static": "^6.12.0", + "@fastify/cookie": "^9.3.1", + "@fastify/formbody": "^7.4.0", + "@fastify/multipart": "^8.1.0", + "ejs": "^3.1.9", + "node-thermal-printer": "^4.4.5", + "better-sqlite3": "^9.4.0", + "node-fetch": "^2.7.0", + "dotenv": "^16.3.1", + "pdf-to-printer": "^5.4.0", + "pdfkit": "^0.15.0", + "serialport": "^12.0.0", + "auto-git-update": "^1.1.1" + } +} + diff --git a/print-worker.js b/print-worker.js new file mode 100644 index 0000000..1227799 --- /dev/null +++ b/print-worker.js @@ -0,0 +1,157 @@ +const config = require('./config'); +const database = require('./database'); +const printer = require('./printer'); + +class PrintQueueWorker { + constructor(db = database, cfg = config, printerManager = printer) { + this.db = db; + this.cfg = cfg; + this.printer = printerManager; + this.intervalId = null; + this.isRunning = false; + this.isProcessing = false; + } + + start(intervalMs = 10000) { + if (this.isRunning) return; + this.isRunning = true; + const configured = parseInt(this.cfg.get('printWorkerInterval'), 10); + const tickMs = Number.isFinite(configured) && configured > 0 ? configured : intervalMs; + console.log(`[PrintQueueWorker] Starting with interval ${tickMs} ms`); + this._schedule(tickMs); + } + + stop() { + this.isRunning = false; + if (this.intervalId) { + clearTimeout(this.intervalId); + this.intervalId = null; + } + console.log('[PrintQueueWorker] Stopped'); + } + + _schedule(intervalMs) { + if (!this.isRunning) return; + this.intervalId = setTimeout(async () => { + try { + await this._tick(); + } catch (err) { + console.error('[PrintQueueWorker] Tick error:', err.message); + } finally { + this._schedule(intervalMs); + } + }, intervalMs); + } + + async _tick() { + if (this.isProcessing) { + return; // avoid overlapping runs + } + this.isProcessing = true; + try { + // Skip if no printers likely reachable to avoid hammering + try { + const printerConfigs = this.db.getEnabledPrinters(); + const anyReachable = await this.printer.anyConfiguredPrinterReachable(printerConfigs); + if (!anyReachable) { + console.log('[PrintQueueWorker] No reachable printers detected yet, will retry later'); + return; + } + } catch (_) { + // If reachability check fails, proceed and let print attempt decide + } + + // Recover stuck processing jobs older than 2 minutes + try { this.db.resetStuckProcessingJobs(120); } catch (_) {} + + const pending = this.db.getPendingPrintJobs(); + if (!pending || pending.length === 0) { + return; + } + console.log(`[PrintQueueWorker] Processing ${pending.length} pending print job(s)`); + + for (const job of pending) { + try { + const order = this.db.getOrderById(job.order_id); + if (!order) { + console.warn(`[PrintQueueWorker] Order ${job.order_id} not found, marking job ${job.id} failed`); + this.db.markPrintJobFailed(job.id); + continue; + } + + // Check 1: If the order has been printed after the job was created, mark job completed to avoid duplicates + if (order.printedAt && job.created_at && Number(order.printedAt) >= Number(job.created_at)) { + console.log(`[PrintQueueWorker] Skipping job ${job.id}, order ${order.id} already printed at ${order.printedAt}`); + this.db.markPrintJobCompleted(job.id); + continue; + } + + // Check 2: Verify there's no active job (pending/processing) for this order+type (within last 60 seconds) + // Do NOT block reprints because of a recently COMPLETED job; users may intentionally reprint. + const activeCheck = this.db.hasActiveOrRecentJob(job.order_id, job.print_type, 60); + if (activeCheck.hasActive && activeCheck.jobId !== job.id) { + // Skip if another active pending/processing job exists + if (activeCheck.status === 'pending' || activeCheck.status === 'processing') { + console.log(`[PrintQueueWorker] Skipping job ${job.id}, another active job ${activeCheck.jobId} (${activeCheck.status}) exists for order ${job.order_id} (${job.print_type})`); + this.db.markPrintJobCompleted(job.id); + continue; + } + // If the recent job is COMPLETED, only block for non-reprint types (avoid duplicate cancellations/new) + if (activeCheck.status === 'completed' && job.print_type !== 'reprint') { + console.log(`[PrintQueueWorker] Skipping job ${job.id}, recent completed job ${activeCheck.jobId} exists for order ${job.order_id} (${job.print_type})`); + this.db.markPrintJobCompleted(job.id); + continue; + } + } + + // Check 3: For cancellations, verify we haven't already printed one + if (job.print_type === 'canceled' && this.db.hasPrintedCancellation(job.order_id)) { + console.log(`[PrintQueueWorker] Skipping job ${job.id}, cancellation already printed for order ${job.order_id}`); + this.db.markPrintJobCompleted(job.id); + continue; + } + + let type = job.print_type; + if (type === 'reprint') { + type = order.localStatus === 'canceled' ? 'canceled' : 'new'; + } + + const printerConfigs = this.db.getEnabledPrinters(); + const cancelReason = (order && (order.cancellationReason || (order.order && order.order.cancellationReason))) || 'Order canceled'; + const options = type === 'canceled' ? { reason: cancelReason } : {}; + + let result; + if (printerConfigs && printerConfigs.length > 0) { + result = await this.printer.printOrderReceiptWithPrinterConfigs(order, printerConfigs, type, options); + } else { + const appConfig = this.cfg.getAll(); + if (!this.printer.printer || !this.printer.config) { + try { this.printer.initializePrinter(appConfig); } catch (_) {} + } + result = await this.printer.printOrderReceipt(order, type, options); + } + + if (result && result.success) { + this.db.markOrderPrinted(order.id); + this.db.markPrintJobCompleted(job.id); + // Cleanup any other pending/processing jobs for this order+type to prevent duplicates + this.db.cleanupDuplicateJobs(job.id, job.order_id, job.print_type); + console.log(`[PrintQueueWorker] ✓ Printed order #${order.id} (job ${job.id})`); + } else { + // Keep as pending for retry on next tick + const errMsg = result && result.error ? result.error : 'Unknown print error'; + console.warn(`[PrintQueueWorker] ✗ Print failed for order #${order.id} (job ${job.id}): ${errMsg}`); + } + } catch (errJob) { + console.error(`[PrintQueueWorker] Job ${job.id} error:`, errJob.message); + } + } + } finally { + this.isProcessing = false; + } + } +} + +module.exports = PrintQueueWorker; + + diff --git a/printer.js b/printer.js new file mode 100644 index 0000000..eb90dbe --- /dev/null +++ b/printer.js @@ -0,0 +1,1757 @@ +const { ThermalPrinter, PrinterTypes } = require('node-thermal-printer'); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const PDFDocument = require('pdfkit'); +const pdfPrinter = require('pdf-to-printer'); +const { SerialPort } = require('serialport'); +let sharp = null; +try { sharp = require('sharp'); } catch (_) {} +let PNGLib = null; +try { PNGLib = require('pngjs').PNG; } catch (_) {} + +class PrinterManager { + constructor() { + this.printer = null; + this.config = null; + this.currentInterface = null; // normalized interface string used by printer lib + this.inFlightJobs = new Map(); // key -> { promise, timestamp } to dedupe concurrent prints + this.recentlyCompleted = new Map(); // key -> timestamp for recently completed jobs + this.cleanupInterval = null; + + // Start periodic cleanup of old entries + this.startCleanupTimer(); + } + + startCleanupTimer() { + // Clean up old in-flight and recently completed entries every 30 seconds + this.cleanupInterval = setInterval(() => { + const now = Date.now(); + const inFlightTimeout = 120000; // 2 minutes for stuck in-flight jobs + const completedTimeout = 60000; // 1 minute for completed jobs tracking + + // Clean up old in-flight jobs (might be stuck) + for (const [key, data] of this.inFlightJobs.entries()) { + if (now - data.timestamp > inFlightTimeout) { + console.log(`[PrinterManager] Cleaning up stuck in-flight job: ${key}`); + this.inFlightJobs.delete(key); + } + } + + // Clean up old recently completed tracking + for (const [key, timestamp] of this.recentlyCompleted.entries()) { + if (now - timestamp > completedTimeout) { + this.recentlyCompleted.delete(key); + } + } + }, 30000); + } + + stopCleanupTimer() { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + this.cleanupInterval = null; + } + } + + initializePrinter(config) { + this.config = config; + + const printerTypeMap = { + 'epson': PrinterTypes.EPSON, + 'star': PrinterTypes.STAR, + 'tanca': PrinterTypes.TANCA, + 'brother': PrinterTypes.BROTHER, + 'custom': PrinterTypes.CUSTOM + }; + + const printerType = printerTypeMap[config.printerType] || PrinterTypes.EPSON; + const printerWidth = parseInt(config.printerWidth, 10) || 48; + + // Normalize interface from config (supports usb/serial/network) + const normalized = this.buildInterfaceFromConfig(config); + this.currentInterface = normalized; + + this.printer = new ThermalPrinter({ + type: printerType, + interface: normalized, + width: printerWidth, + characterSet: 'PC437_USA', + removeSpecialCharacters: false, + lineCharacter: config.lineStyle === 'double' ? '=' : (config.lineStyle === 'dashed' ? '-' : '-') + }); + + console.log('Printer initialized:', { + type: config.printerType, + interface: normalized, + width: printerWidth + }); + } + // Ensure only one print per (orderId,type) runs at a time; others await the same promise + // Also track recently completed jobs to prevent rapid duplicate submissions + async _withInflight(key, executor, cooldownMs = 30000) { + try { + const now = Date.now(); + + // Check if there's an active in-flight job + if (this.inFlightJobs.has(key)) { + const existing = this.inFlightJobs.get(key); + console.log(`[PrinterManager] Reusing in-flight job for ${key}`); + return await existing.promise; + } + + // Check if this job was recently completed (within the configured cooldown) + if (this.recentlyCompleted.has(key)) { + const completedTime = this.recentlyCompleted.get(key); + const timeSince = now - completedTime; + if (timeSince < cooldownMs) { + console.log(`[PrinterManager] Skipping duplicate print for ${key} (completed ${Math.floor(timeSince/1000)}s ago)`); + return { success: true, message: 'Duplicate print prevented - recently completed' }; + } + } + + const p = (async () => { + try { + const result = await executor(); + // Track successful completion + if (result && result.success) { + this.recentlyCompleted.set(key, Date.now()); + } + return result; + } finally { + this.inFlightJobs.delete(key); + } + })(); + + this.inFlightJobs.set(key, { promise: p, timestamp: now }); + return await p; + } catch (err) { + throw err; + } + } + + + buildInterfaceFromConfig(config) { + const iface = (config.printerInterface || '').toLowerCase(); + const rawPath = (config.printerPath || '').trim(); + + // Network: accept "ip" or "ip:port" and prefix tcp:// + if (iface === 'network') { + if (!rawPath) return 'tcp://127.0.0.1:9100'; + if (rawPath.startsWith('tcp://')) return rawPath; + // If port not provided, default 9100 + const hasPort = rawPath.includes(':'); + return hasPort ? `tcp://${rawPath}` : `tcp://${rawPath}:9100`; + } + + // Serial on Windows needs \\ \\ . \\ COMX + if (iface === 'serial') { + // If looks like COMx, normalize, else pass-through (linux e.g. /dev/ttyS0) + if (/^COM\d+$/i.test(rawPath)) { + return `\\\\.\\${rawPath.toUpperCase()}`; + } + return rawPath || 'COM1'; + } + + // Default USB or direct path + return rawPath || '/dev/usb/lp0'; + } + + // Helper to safely call printer APIs that may not exist in some printer profiles + safeCall(methodName, ...args) { + try { + if (!this.printer) return; + const fn = this.printer[methodName]; + if (typeof fn === 'function') { + return fn.apply(this.printer, args); + } + } catch (err) { + console.warn(`Printer method ${methodName} failed:`, err.message); + } + } + + // Helper to safely call a specific printer instance + safeCallOn(instance, methodName, ...args) { + try { + if (!instance) return; + const fn = instance[methodName]; + if (typeof fn === 'function') { + return fn.apply(instance, args); + } + } catch (err) { + console.warn(`Printer method ${methodName} failed:`, err.message); + } + } + + // Direct execute for COM ports to bypass library-side connectivity quirks on Windows + async executeDirectWrite() { + return new Promise((resolve, reject) => { + try { + if (!this.printer) return reject(new Error('Printer not initialized')); + + const buffer = this.printer.getBuffer(); + const comPath = this.currentInterface && this.currentInterface.startsWith('\\\\.\\') + ? this.currentInterface + : null; + + if (!comPath) { + return reject(new Error('Direct write is only for COM ports')); + } + + const port = new SerialPort({ + path: comPath, + baudRate: 115200, + autoOpen: false + }); + + port.on('error', (err) => { + reject(err); + }); + + port.open((err) => { + if (err) { + reject(new Error(`Failed to open serial port: ${err.message}`)); + return; + } + + port.write(buffer, (err2) => { + if (err2) { + try { port.close(); } catch (_) {} + reject(new Error(`Failed to write to printer: ${err2.message}`)); + } else { + setTimeout(() => { + try { + port.close(() => resolve(true)); + } catch (_) { + resolve(true); + } + }, 1000); + } + }); + }); + } catch (error) { + reject(error); + } + }); + } + + // Direct write for a specific printer instance and COM path + async executeDirectWriteFor(instance, comPath) { + return new Promise((resolve, reject) => { + try { + if (!instance) return reject(new Error('Printer not initialized')); + + const buffer = instance.getBuffer(); + if (!comPath) { + return reject(new Error('Direct write is only for COM ports')); + } + + const port = new SerialPort({ + path: comPath, + baudRate: 9600, + autoOpen: false + }); + + port.on('error', (err) => { + reject(err); + }); + + port.open((err) => { + if (err) { + reject(new Error(`Failed to open serial port: ${err.message}`)); + return; + } + + port.write(buffer, (err2) => { + if (err2) { + try { port.close(); } catch (_) {} + reject(new Error(`Failed to write to printer: ${err2.message}`)); + } else { + setTimeout(() => { + try { + port.close(() => resolve(true)); + } catch (_) { + resolve(true); + } + }, 1000); + } + }); + }); + } catch (error) { + reject(error); + } + }); + } + + // Build list of target interfaces from config.selectedPrintersJson + // If empty, falls back to main printer config + getSelectedTargets() { + const targets = []; + try { + const raw = this.config && this.config.selectedPrintersJson; + if (!raw) return targets; + const arr = typeof raw === 'string' ? JSON.parse(raw) : raw; + if (!Array.isArray(arr)) return targets; + arr.forEach(t => { + if (!t || !t.type || !t.interface) return; + const norm = this.normalizeTargetInterface(t.type, t.interface); + if (norm && (t.type === 'network' || t.type === 'com' || t.type === 'serial' || t.type === 'usb' || t.type === 'system')) { + targets.push({ type: t.type, interface: norm }); + } + }); + // Dedupe by type+normalized interface to avoid double prints + const seen = new Set(); + const unique = []; + for (const t of targets) { + const key = `${String(t.type).toLowerCase()}|${String(t.interface)}`; + if (seen.has(key)) continue; + seen.add(key); + unique.push(t); + } + return unique; + } catch (e) { + console.warn('Failed to parse selectedPrintersJson:', e.message); + } + return targets; + } + + // Build a target object from main printer config + getMainPrinterTarget() { + try { + if (!this.config || !this.config.printerInterface || !this.config.printerPath) { + return null; + } + const iface = (this.config.printerInterface || '').toLowerCase(); + let type = iface; // network, serial, usb + if (iface === 'serial') type = 'com'; + const norm = this.normalizeTargetInterface(type, this.config.printerPath); + if (!norm) return null; + return { type, interface: norm }; + } catch (e) { + return null; + } + } + + // Check if main printer is included in multi-printer selection + isMainPrinterInSelection(targets) { + const mainTarget = this.getMainPrinterTarget(); + if (!mainTarget) return false; + return targets.some(t => + t.type === mainTarget.type && t.interface === mainTarget.interface + ); + } + + normalizeTargetInterface(type, value) { + const val = String(value || '').trim(); + if (!val) return null; + const t = String(type || '').toLowerCase(); + if (t === 'network') { + if (val.startsWith('tcp://')) return val; + const hasPort = val.includes(':'); + return hasPort ? `tcp://${val}` : `tcp://${val}:9100`; + } + if (t === 'com' || t === 'serial') { + if (/^\\\\\.\\COM\d+$/i.test(val)) return val; + if (/^COM\d+$/i.test(val)) return `\\\\.\\${val.toUpperCase()}`; + return val; // linux tty path + } + if (t === 'usb') { + return val; // e.g., /dev/usb/lp0 + } + if (t === 'system') { + return val; // spooler printer name + } + return null; + } + + createPrinterForInterface(normalizedInterface) { + const printerTypeMap = { + 'epson': PrinterTypes.EPSON, + 'star': PrinterTypes.STAR, + 'tanca': PrinterTypes.TANCA, + 'brother': PrinterTypes.BROTHER, + 'custom': PrinterTypes.CUSTOM + }; + const printerType = printerTypeMap[this.config.printerType] || PrinterTypes.EPSON; + const printerWidth = parseInt(this.config.printerWidth, 10) || 48; + return new ThermalPrinter({ + type: printerType, + interface: normalizedInterface, + width: printerWidth, + characterSet: 'PC437_USA', + removeSpecialCharacters: false, + lineCharacter: this.config.lineStyle === 'double' ? '=' : (this.config.lineStyle === 'dashed' ? '-' : '-') + }); + } + + // Create a printer instance from a full printer configuration object + createPrinterInstanceFromConfig(printerConfig) { + const printerTypeMap = { + 'epson': PrinterTypes.EPSON, + 'star': PrinterTypes.STAR, + 'tanca': PrinterTypes.TANCA, + 'brother': PrinterTypes.BROTHER, + 'custom': PrinterTypes.CUSTOM + }; + + const printerType = printerTypeMap[printerConfig.printer_type] || PrinterTypes.EPSON; + const printerWidth = parseInt(printerConfig.paper_width, 10) || 48; + + // Normalize the interface based on type + let normalizedInterface = printerConfig.interface; + if (printerConfig.type === 'network') { + if (!normalizedInterface.startsWith('tcp://')) { + const hasPort = normalizedInterface.includes(':'); + normalizedInterface = hasPort ? `tcp://${normalizedInterface}` : `tcp://${normalizedInterface}:9100`; + } + } else if (printerConfig.type === 'com' || printerConfig.type === 'serial') { + if (/^COM\d+$/i.test(normalizedInterface)) { + normalizedInterface = `\\\\.\\${normalizedInterface.toUpperCase()}`; + } + } + + return new ThermalPrinter({ + type: printerType, + interface: normalizedInterface, + width: printerWidth, + characterSet: 'PC437_USA', + removeSpecialCharacters: false, + lineCharacter: printerConfig.line_style === 'double' ? '=' : (printerConfig.line_style === 'dashed' ? '-' : '-') + }); + } + + // Get printer max dots width from config + getPrinterMaxDotsWidthFromConfig(cfg) { + if (cfg.logo_max_width_dots && Number.isFinite(Number(cfg.logo_max_width_dots))) { + const n = Number(cfg.logo_max_width_dots); + if (n > 0) return n; + } + const widthChars = parseInt(cfg.paper_width, 10) || 48; + if (widthChars >= 48) return 576; // typical 80mm + if (widthChars >= 42) return 512; + return 384; // typical 58mm + } + + async executeForInstance(instance, normalizedInterface) { + if (normalizedInterface && /^\\\\\.\\COM\d+/i.test(normalizedInterface)) { + await this.executeDirectWriteFor(instance, normalizedInterface); + } else { + await instance.execute(); + } + } + + getPrinterMaxDotsWidth() { + const cfg = this.config || {}; + if (cfg.logoMaxWidthDots && Number.isFinite(Number(cfg.logoMaxWidthDots))) { + const n = Number(cfg.logoMaxWidthDots); + if (n > 0) return n; + } + const widthChars = parseInt(cfg.printerWidth, 10) || 48; + if (widthChars >= 48) return 576; // typical 80mm + if (widthChars >= 42) return 512; + return 384; // typical 58mm + } + + async printLogoWithFit(instance, logoPath) { + try { + if (!logoPath || !fs.existsSync(logoPath)) return false; + const maxWidth = this.getPrinterMaxDotsWidth(); + this.safeCallOn(instance, 'alignCenter'); + + // Prefer sharp when available (handles PNG/JPEG/GIF and resizes well) + if (sharp) { + try { + const resized = await sharp(logoPath) + .resize({ width: maxWidth, fit: 'inside', withoutEnlargement: true }) + .png() + .toBuffer(); + await instance.printImageBuffer(resized); + instance.newLine(); + return true; + } catch (e) { + console.warn('Sharp resize failed, falling back:', e.message); + } + } + + // Fallback for PNG using pngjs and nearest-neighbor downscale + const lower = logoPath.toLowerCase(); + if (PNGLib && (lower.endsWith('.png'))) { + try { + const input = fs.readFileSync(logoPath); + const png = PNGLib.sync.read(input); + if (png.width <= maxWidth) { + await instance.printImageBuffer(input); + instance.newLine(); + return true; + } + const ratio = maxWidth / png.width; + const targetW = Math.max(1, Math.floor(png.width * ratio)); + const targetH = Math.max(1, Math.floor(png.height * ratio)); + const out = new PNGLib({ width: targetW, height: targetH }); + for (let y2 = 0; y2 < targetH; y2++) { + const y1 = Math.floor(y2 / ratio); + for (let x2 = 0; x2 < targetW; x2++) { + const x1 = Math.floor(x2 / ratio); + const idx1 = (png.width * y1 + x1) << 2; + const idx2 = (targetW * y2 + x2) << 2; + out.data[idx2] = png.data[idx1]; + out.data[idx2 + 1] = png.data[idx1 + 1]; + out.data[idx2 + 2] = png.data[idx1 + 2]; + out.data[idx2 + 3] = png.data[idx1 + 3]; + } + } + const buf = PNGLib.sync.write(out); + await instance.printImageBuffer(buf); + instance.newLine(); + return true; + } catch (e) { + console.warn('PNG fallback resize failed:', e.message); + } + } + + // Last resort: try library native path (may crop if too wide) + await instance.printImage(logoPath); + instance.newLine(); + return true; + } catch (err) { + console.error('Failed to print logo:', err.message); + return false; + } + } + + async printLogoWithFitCustom(instance, logoPath, maxWidth) { + try { + if (!logoPath || !fs.existsSync(logoPath)) return false; + this.safeCallOn(instance, 'alignCenter'); + + // Prefer sharp when available (handles PNG/JPEG/GIF and resizes well) + if (sharp) { + try { + const resized = await sharp(logoPath) + .resize({ width: maxWidth, fit: 'inside', withoutEnlargement: true }) + .png() + .toBuffer(); + await instance.printImageBuffer(resized); + instance.newLine(); + return true; + } catch (e) { + console.warn('Sharp resize failed, falling back:', e.message); + } + } + + // Fallback for PNG using pngjs and nearest-neighbor downscale + const lower = logoPath.toLowerCase(); + if (PNGLib && (lower.endsWith('.png'))) { + try { + const input = fs.readFileSync(logoPath); + const png = PNGLib.sync.read(input); + if (png.width <= maxWidth) { + await instance.printImageBuffer(input); + instance.newLine(); + return true; + } + const ratio = maxWidth / png.width; + const targetW = Math.max(1, Math.floor(png.width * ratio)); + const targetH = Math.max(1, Math.floor(png.height * ratio)); + const out = new PNGLib({ width: targetW, height: targetH }); + for (let y2 = 0; y2 < targetH; y2++) { + const y1 = Math.floor(y2 / ratio); + for (let x2 = 0; x2 < targetW; x2++) { + const x1 = Math.floor(x2 / ratio); + const idx1 = (png.width * y1 + x1) << 2; + const idx2 = (targetW * y2 + x2) << 2; + out.data[idx2] = png.data[idx1]; + out.data[idx2 + 1] = png.data[idx1 + 1]; + out.data[idx2 + 2] = png.data[idx1 + 2]; + out.data[idx2 + 3] = png.data[idx1 + 3]; + } + } + const buf = PNGLib.sync.write(out); + await instance.printImageBuffer(buf); + instance.newLine(); + return true; + } catch (e) { + console.warn('PNG fallback resize failed:', e.message); + } + } + + // Last resort: try library native path (may crop if too wide) + await instance.printImage(logoPath); + instance.newLine(); + return true; + } catch (err) { + console.error('Failed to print logo:', err.message); + return false; + } + } + + buildQrContent(template, order) { + try { + const tpl = (template || 'ORDER-{id}').toString(); + const createdAt = order && order.createdAt ? new Date(order.createdAt * 1000).toISOString() : new Date().toISOString(); + const type = order && order.order && order.order.type ? order.order.type : ''; + const total = (order && (order.totalAmount || (order.order && order.order.amount))) || 0; + return tpl + .replace(/\{id\}/g, String(order && order.id || '')) + .replace(/\{total\}/g, String(total)) + .replace(/\{type\}/g, String(type)) + .replace(/\{createdAt\}/g, String(createdAt)); + } catch (_) { + return `ORDER-${order && order.id ? order.id : ''}`; + } + } + + // Get optimal column widths based on printer width + getColumnWidths(config) { + const width = parseInt(config.paper_width || config.printerWidth, 10) || 48; + + // For narrow receipts (58mm / 32 chars), use tighter layout + if (width <= 32) { + // Total: 0.95 (5% buffer to ensure no edge wrapping) + return { + useTable: true, + itemWidth: 0.55, // 55% for item name + priceWidth: 0.40, // 40% for price (generous space to prevent wrapping) + priceMinChars: 8 // Minimum chars needed for price column + }; + } + // For standard receipts (80mm / 48 chars), use balanced layout + else if (width <= 48) { + // Total: 0.95 (5% buffer to ensure no edge wrapping) + return { + useTable: true, + itemWidth: 0.60, // 60% for item name + priceWidth: 0.35, // 35% for price (good space to prevent wrapping) + priceMinChars: 10 // Minimum chars needed for price column + }; + } + // For wide receipts (letter / 80 chars), use spacious layout + else { + // Total: 0.95 (5% buffer to ensure no edge wrapping) + return { + useTable: true, + itemWidth: 0.65, // 65% for item name + priceWidth: 0.30, // 30% for price (adequate space) + priceMinChars: 12 // Minimum chars needed for price column + }; + } + } + + // Truncate text if needed to fit in column + truncateText(text, maxLength) { + if (!text || text.length <= maxLength) return text; + return text.substring(0, maxLength - 3) + '...'; + } + + async renderTest(instance) { + this.safeCallOn(instance, 'clear'); + this.safeCallOn(instance, 'alignCenter'); + instance.println('TEST PRINT'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'alignLeft'); + instance.println('Printer Type: ' + (this.config.printerType || 'N/A')); + instance.println('Interface: ' + (instance.interface || this.currentInterface || this.config.printerPath || 'N/A')); + instance.println('Width: ' + (this.config.printerWidth || 'N/A') + ' chars'); + instance.println('Time: ' + new Date().toLocaleString()); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'alignCenter'); + instance.println('Test Successful'); + this.safeCallOn(instance, 'cut'); + } + + async renderReceipt(instance, order, type = 'new', options = {}, cfg = null) { + // Use provided config or fallback to this.config + // Merge in global toggles for backward compatibility when per-printer config omits them + const base = cfg || this.config || {}; + const globalCfg = this.config || {}; + const config = { + ...base, + show_customer_info: typeof base.show_customer_info !== 'undefined' ? base.show_customer_info : undefined, + show_order_items: typeof base.show_order_items !== 'undefined' ? base.show_order_items : undefined, + show_prices: typeof base.show_prices !== 'undefined' ? base.show_prices : undefined, + show_timestamps: typeof base.show_timestamps !== 'undefined' ? base.show_timestamps : undefined, + // Fallback to global string flags if per-printer booleans are not provided + showCustomerInfo: typeof base.show_customer_info === 'undefined' && typeof base.showCustomerInfo === 'undefined' ? globalCfg.showCustomerInfo : base.showCustomerInfo, + showOrderItems: typeof base.show_order_items === 'undefined' && typeof base.showOrderItems === 'undefined' ? globalCfg.showOrderItems : base.showOrderItems, + showPrices: typeof base.show_prices === 'undefined' && typeof base.showPrices === 'undefined' ? globalCfg.showPrices : base.showPrices, + showTimestamps: typeof base.show_timestamps === 'undefined' && typeof base.showTimestamps === 'undefined' ? globalCfg.showTimestamps : base.showTimestamps + }; + // Reset buffer + this.safeCallOn(instance, 'clear'); + + // Logo (resized to fit) - use config-specific logo path and max width + if (config.logo_path && fs.existsSync(config.logo_path)) { + const maxWidth = this.getPrinterMaxDotsWidthFromConfig(config); + await this.printLogoWithFitCustom(instance, config.logo_path, maxWidth); + } else if (config.logoPath && fs.existsSync(config.logoPath)) { + // Backward compatibility with old config key + await this.printLogoWithFit(instance, config.logoPath); + } + + // Business header + this.safeCallOn(instance, 'alignCenter'); + const businessName = config.business_name || config.businessName; + const paperWidth = parseInt(config.paper_width || config.printerWidth, 10) || 48; + + if (businessName) { + // Adjust text size based on business name length and paper width + const nameLength = businessName.length; + const maxCharsAtSize1 = paperWidth / 2; // Approximate chars at 2x size + + // Use smaller text for long names on narrow paper + if (nameLength > maxCharsAtSize1 && paperWidth <= 32) { + this.safeCallOn(instance, 'setTextNormal'); + this.safeCallOn(instance, 'bold', true); + instance.println(businessName); + this.safeCallOn(instance, 'bold', false); + } else { + this.safeCallOn(instance, 'setTextSize', 1, 1); + this.safeCallOn(instance, 'bold', true); + instance.println(businessName); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + } + this.safeCallOn(instance, 'newLine'); + } + + const contactLines = [ + config.business_address || config.businessAddress, + config.business_phone || config.businessPhone, + config.business_website || config.businessWebsite, + config.business_email || config.businessEmail + ].filter(Boolean); + const contactSize = config.business_contact_size || config.businessContactSize || 'normal'; + + if (contactLines.length > 0) { + // For narrow paper, force normal size to avoid overflow + if (paperWidth <= 32 && contactSize === 'large') { + this.safeCallOn(instance, 'setTextNormal'); + } else if (contactSize === 'large') { + this.safeCallOn(instance, 'setTextSize', 1, 1); + } else { + this.safeCallOn(instance, 'setTextNormal'); + } + contactLines.forEach(line => instance.println(line)); + // Reset size after contacts + this.safeCallOn(instance, 'setTextNormal'); + } + + // Optional header text + const headerText = config.header_text || config.headerText; + if (headerText) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'bold', true); + instance.println(headerText); + this.safeCallOn(instance, 'bold', false); + } + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'newLine'); + + // Order type indicator + if (type === 'canceled') { + this.safeCallOn(instance, 'bold', true); + this.safeCallOn(instance, 'setTextSize', 1, 1); + instance.println('*** ORDER CANCELED ***'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + this.safeCallOn(instance, 'newLine'); + if (options && options.reason) { + this.safeCallOn(instance, 'alignLeft'); + instance.println('Cancellation reason:'); + this.safeCallOn(instance, 'bold', true); + instance.println(String(options.reason)); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'newLine'); + } + } else { + this.safeCallOn(instance, 'bold', true); + this.safeCallOn(instance, 'setTextSize', 1, 1); + instance.println('NEW ORDER'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + this.safeCallOn(instance, 'newLine'); + } + + // Order details + this.safeCallOn(instance, 'alignLeft'); + this.safeCallOn(instance, 'setTextSize', 1, 1); + this.safeCallOn(instance, 'bold', true); + instance.println(`Order #${order.id}`); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + this.safeCallOn(instance, 'newLine'); + + const showTimestamps = (config.show_timestamps !== false) && (config.showTimestamps !== 'false'); + if (showTimestamps) { + instance.println(`Time: ${new Date(order.createdAt * 1000).toLocaleString()}`); + } + + instance.println(`Type: ${order.order.type || 'N/A'}`); + + // Customer info + const showCustomerInfo = (config.show_customer_info !== false) && (config.showCustomerInfo !== 'false'); + if (showCustomerInfo) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'bold', true); + instance.println('CUSTOMER INFO:'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'newLine'); + instance.println(`Name: ${order.customer.name || 'N/A'}`); + if (order.customer.phoneNumber) instance.println(`Phone: ${order.customer.phoneNumber}`); + if (order.order.deliveryAddress) { + instance.println(`Address: ${order.order.deliveryAddress}`); + } + } + + // Items + const showOrderItems = (config.show_order_items !== false) && (config.showOrderItems !== 'false'); + if (showOrderItems) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'bold', true); + instance.println('ORDER ITEMS:'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'newLine'); + + if (order.order.items && Array.isArray(order.order.items)) { + const showPrices = (config.show_prices !== false) && (config.showPrices !== 'false'); + + // Get optimal column widths for this printer + const colWidths = this.getColumnWidths(config); + + // Ensure proper alignment for tableCustom + this.safeCallOn(instance, 'alignLeft'); + + order.order.items.forEach(item => { + const itemName = item.itemName || item.name || 'Unknown Item'; + const qty = item.qty || 1; + const price = item.price || 0; + const totalItemPrice = (price * qty).toFixed(2); + + // Use tableCustom for proper alignment of item and price + if (showPrices) { + try { + // Ensure price text doesn't have extra spaces + const priceText = `$${totalItemPrice}`.trim(); + instance.tableCustom([ + { text: `${qty}x ${itemName}`, align: "LEFT", width: colWidths.itemWidth }, + { text: priceText, align: "RIGHT", width: colWidths.priceWidth } + ]); + } catch (e) { + // Fallback if tableCustom is not available + instance.println(`${qty}x ${itemName} - $${totalItemPrice}`); + } + } else { + instance.println(`${qty}x ${itemName}`); + } + + // Print addons with indentation + if (item.addons && Array.isArray(item.addons) && item.addons.length > 0) { + item.addons.forEach(addon => { + const addonName = addon.name || addon; + if (showPrices && addon.price && addon.price > 0) { + const addonPrice = (addon.price || 0).toFixed(2); + // Ensure price text doesn't have extra spaces + const priceText = `$${addonPrice}`.trim(); + try { + instance.tableCustom([ + { text: ` + ${addonName}`, align: "LEFT", width: colWidths.itemWidth }, + { text: priceText, align: "RIGHT", width: colWidths.priceWidth } + ]); + } catch (e) { + instance.println(` + ${addonName} - $${addonPrice}`); + } + } else { + instance.println(` + ${addonName}`); + } + }); + } + + // Print exclusions with indentation + if (item.exclude && Array.isArray(item.exclude) && item.exclude.length > 0) { + item.exclude.forEach(ex => { + const excludeName = ex.name || ex; + instance.println(` - NO ${excludeName}`); + }); + } + }); + } + } + + // Special instructions + if (order.order.specialInstructions) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'bold', true); + instance.println('SPECIAL INSTRUCTIONS:'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'newLine'); + instance.println(order.order.specialInstructions); + } + + // Delivery instructions + if (order.order.deliveryInstructions) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'bold', true); + instance.println('DELIVERY INSTRUCTIONS:'); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'newLine'); + instance.println(order.order.deliveryInstructions); + } + + // Food allergy + if (order.order.foodAllergy) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'alignCenter'); + this.safeCallOn(instance, 'setTextSize', 1, 1); + this.safeCallOn(instance, 'bold', true); + this.safeCallOn(instance, 'invert', true); + instance.println(' FOOD ALLERGY WARNING '); + this.safeCallOn(instance, 'invert', false); + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'alignLeft'); + if (order.order.foodAllergyNotes) { + instance.println(order.order.foodAllergyNotes.toUpperCase()); + } + } + + // Total + const showPricesForTotal = (config.show_prices !== false) && (config.showPrices !== 'false'); + if (showPricesForTotal) { + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'alignLeft'); // Ensure proper alignment for tableCustom + //this.safeCallOn(instance, 'setTextSize', 1, 1); + // Price breakdown (normal font, right-aligned): Subtotal, Tax, Delivery Fee + // Note: Keep TOTAL styling unchanged below + try { + // Extract values with fallbacks to support multiple payload shapes + const rawSubtotal = (order && typeof order.amount !== 'undefined') + ? order.amount + : (order && order.order && typeof order.order.amount !== 'undefined' ? order.order.amount : null); + const rawTaxAmount = (order && typeof order.taxAmount !== 'undefined') + ? order.taxAmount + : (order && order.order && typeof order.order.taxAmount !== 'undefined' ? order.order.taxAmount : null); + const rawTaxRate = (order && typeof order.taxRate !== 'undefined') + ? order.taxRate + : (order && order.order && typeof order.order.taxRate !== 'undefined' ? order.order.taxRate : null); + const rawDelivery = (order && typeof order.deliveryFee !== 'undefined') + ? order.deliveryFee + : (order && order.order && typeof order.order.deliveryFee !== 'undefined' ? order.order.deliveryFee : null); + + this.safeCallOn(instance, 'alignRight'); + if (typeof rawSubtotal === 'number' && isFinite(rawSubtotal)) { + instance.println(`Subtotal: $${rawSubtotal.toFixed(2)}`); + } + if (typeof rawTaxAmount === 'number' && isFinite(rawTaxAmount) && rawTaxAmount > 0) { + let taxLabel = 'Tax'; + if (typeof rawTaxRate === 'number' && isFinite(rawTaxRate) && rawTaxRate > 0) { + const rateStr = Number(rawTaxRate).toFixed(2).replace(/\.?0+$/, ''); + taxLabel = `Tax (${rateStr}%)`; + } + instance.println(`${taxLabel}: $${rawTaxAmount.toFixed(2)}`); + } + if (typeof rawDelivery === 'number' && isFinite(rawDelivery) && rawDelivery > 0) { + instance.println(`Delivery Fee: $${rawDelivery.toFixed(2)}`); + } + } catch (_) {} + this.safeCallOn(instance, 'setTextSize', 1, 1); + this.safeCallOn(instance, 'bold', true); + this.safeCallOn(instance, 'alignRight'); + const totalAmount = (order.totalAmount || 0).toFixed(2); + instance.println(`TOTAL: $${totalAmount}`); + + this.safeCallOn(instance, 'bold', false); + this.safeCallOn(instance, 'setTextNormal'); + } + + // QR code + const qrCodeEnabled = config.qr_code_enabled !== false && config.qrCodeEnabled !== 'false'; + if (qrCodeEnabled) { + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'alignCenter'); + try { + const qrSize = config.qr_code_size || config.qrCodeSize || 3; + let sizeRaw = parseInt(qrSize, 10); + + // Auto-adjust QR size based on paper width if not explicitly set + const paperWidth = parseInt(config.paper_width || config.printerWidth, 10) || 48; + if (isNaN(sizeRaw) || sizeRaw <= 0) { + // Default sizes based on paper width + if (paperWidth <= 32) { + sizeRaw = 2; // Small QR for narrow paper + } else if (paperWidth <= 48) { + sizeRaw = 3; // Medium QR for standard paper + } else { + sizeRaw = 4; // Larger QR for wide paper + } + } + + const size = Math.min(8, Math.max(2, sizeRaw)); + const correction = config.qr_code_correction || config.qrCodeCorrection || 'M'; + const template = config.qr_code_content_template || config.qrCodeContentTemplate || 'ORDER-{id}'; + const content = this.buildQrContent(template, order); + instance.printQR(content, { cellSize: size, correction, model: 2 }); + } catch (error) { + console.error('Failed to print QR code:', error.message); + } + } + + // Footer + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'alignCenter'); + const footerText = (typeof config.footer_text !== 'undefined' ? config.footer_text : config.footerText); + if (footerText) { + this.safeCallOn(instance, 'bold', true); + instance.println(footerText); + this.safeCallOn(instance, 'bold', false); + } + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'newLine'); + this.safeCallOn(instance, 'cut'); + } + + async testPrint() { + if (!this.printer) { + throw new Error('Printer not initialized'); + } + + const targets = this.getSelectedTargets(); + console.log('[PrinterManager] Test print - Selected targets:', targets.length); + + if (targets.length === 0) { + // Single/fallback printer path using main printer config + console.log('[PrinterManager] Using single/fallback printer:', this.currentInterface); + this.safeCall('clear'); + await this.renderTest(this.printer); + try { + if (this.currentInterface && this.currentInterface.startsWith('\\\\.\\')) { + await this.executeDirectWrite(); + } else { + await this.printer.execute(); + } + console.log('[PrinterManager] Test print successful (single printer)'); + return { success: true }; + } catch (error) { + console.error('[PrinterManager] Test print failed:', error.message); + return { success: false, error: error.message }; + } + } + + // Multi-printer: fan out concurrently + console.log('[PrinterManager] Printing to', targets.length, 'printer(s)'); + const jobs = targets.map(async (t, idx) => { + console.log(`[PrinterManager] Test print job ${idx + 1}/${targets.length}: ${t.type} -> ${t.interface}`); + if (t.type === 'system') { + await this.printSystemTest(t.interface); + } else { + const p = this.createPrinterForInterface(t.interface); + await this.renderTest(p); + await this.executeForInstance(p, t.interface); + } + }); + const results = await Promise.allSettled(jobs); + const succeeded = results.filter(r => r.status === 'fulfilled').length; + const failed = results.length - succeeded; + console.log(`[PrinterManager] Test print results: ${succeeded} succeeded, ${failed} failed`); + + if (succeeded === 0) { + const firstErr = results.find(r => r.status === 'rejected'); + return { success: false, error: firstErr && firstErr.reason ? firstErr.reason.message || String(firstErr.reason) : 'All test prints failed' }; + } + return { success: true, message: `Test sent to ${succeeded} printer(s)` + (failed > 0 ? `, ${failed} failed` : '') }; + } + + // Test print with specific printer configuration + async testPrintWithConfig(printerConfig) { + try { + console.log(`[PrinterManager] Test print for: ${printerConfig.name} (${printerConfig.type}:${printerConfig.interface})`); + + if (printerConfig.type === 'system') { + await this.printSystemTest(printerConfig.interface); + return { success: true, message: 'Test sent to system printer' }; + } + + // Create printer instance from config + const instance = this.createPrinterInstanceFromConfig(printerConfig); + + // Render test receipt + this.safeCallOn(instance, 'clear'); + this.safeCallOn(instance, 'alignCenter'); + instance.println('TEST PRINT'); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'alignLeft'); + instance.println(`Printer: ${printerConfig.name}`); + instance.println(`Type: ${printerConfig.printer_type}`); + instance.println(`Paper: ${printerConfig.paper_format} (${printerConfig.paper_width} chars)`); + instance.println(`Interface: ${printerConfig.interface}`); + instance.println(`Time: ${new Date().toLocaleString()}`); + this.safeCallOn(instance, 'drawLine'); + this.safeCallOn(instance, 'alignCenter'); + instance.println('Test Successful'); + this.safeCallOn(instance, 'cut'); + + // Execute print + let normalizedInterface = printerConfig.interface; + if (printerConfig.type === 'network') { + if (!normalizedInterface.startsWith('tcp://')) { + const hasPort = normalizedInterface.includes(':'); + normalizedInterface = hasPort ? `tcp://${normalizedInterface}` : `tcp://${normalizedInterface}:9100`; + } + } else if (printerConfig.type === 'com' || printerConfig.type === 'serial') { + if (/^COM\d+$/i.test(normalizedInterface)) { + normalizedInterface = `\\\\.\\${normalizedInterface.toUpperCase()}`; + } + } + + await this.executeForInstance(instance, normalizedInterface); + + console.log(`[PrinterManager] Test print successful for: ${printerConfig.name}`); + return { success: true, message: `Test sent to ${printerConfig.name}` }; + } catch (error) { + console.error(`[PrinterManager] Test print failed for ${printerConfig.name}:`, error.message); + return { success: false, error: error.message }; + } + } + + async printOrderReceipt(order, type = 'new', options = {}) { + if (!this.printer) { + throw new Error('Printer not initialized'); + } + + const key = `order:${order && order.id}:${type}`; + const cooldownMs = (options && typeof options.cooldownMs === 'number') ? options.cooldownMs : 30000; + return await this._withInflight(key, async () => { + try { + const targets = this.getSelectedTargets(); + console.log(`[PrinterManager] Print order #${order.id} - Selected targets:`, targets.length); + + if (targets.length === 0) { + // Single/fallback printer path using main printer config + console.log(`[PrinterManager] Printing order #${order.id} to single/fallback printer:`, this.currentInterface); + await this.renderReceipt(this.printer, order, type, options); + // Execute print with one-time reinitialize-on-failure retry + try { + if (this.currentInterface && this.currentInterface.startsWith('\\\\.\\')) { + await this.executeDirectWrite(); + } else { + await this.printer.execute(); + } + } catch (execErr) { + console.warn('[PrinterManager] Execute failed, attempting reinitialize and retry:', execErr.message); + try { + if (this.config) { + this.initializePrinter(this.config); + await this.renderReceipt(this.printer, order, type, options); + if (this.currentInterface && this.currentInterface.startsWith('\\\\.\\')) { + await this.executeDirectWrite(); + } else { + await this.printer.execute(); + } + } else { + throw execErr; + } + } catch (retryErr) { + throw retryErr; + } + } + console.log(`[PrinterManager] Receipt printed for order #${order.id}`); + return { success: true }; + } + + // Multi-printer + console.log(`[PrinterManager] Printing order #${order.id} to ${targets.length} printer(s)`); + const jobs = targets.map(async (t, idx) => { + console.log(`[PrinterManager] Print job ${idx + 1}/${targets.length} for order #${order.id}: ${t.type} -> ${t.interface}`); + if (t.type === 'system') { + await this.printSystemReceipt(order, type, options, t.interface); + } else { + const p = this.createPrinterForInterface(t.interface); + await this.renderReceipt(p, order, type, options); + try { + await this.executeForInstance(p, t.interface); + } catch (multiErr) { + console.warn(`[PrinterManager] Execute failed for ${t.interface}, retrying after reinit:`, multiErr.message); + // Recreate instance and retry once + const p2 = this.createPrinterForInterface(t.interface); + await this.renderReceipt(p2, order, type, options); + await this.executeForInstance(p2, t.interface); + } + } + }); + const results = await Promise.allSettled(jobs); + const succeeded = results.filter(r => r.status === 'fulfilled').length; + const failed = results.length - succeeded; + console.log(`[PrinterManager] Print results for order #${order.id}: ${succeeded} succeeded, ${failed} failed`); + + if (succeeded === 0) { + const firstErr = results.find(r => r.status === 'rejected'); + return { success: false, error: firstErr && firstErr.reason ? firstErr.reason.message || String(firstErr.reason) : 'All prints failed' }; + } + return { success: true }; + } catch (error) { + console.error('[PrinterManager] Print error:', error.message); + return { success: false, error: error.message }; + } + }, cooldownMs); + } + + // Print order with per-printer configurations from database + async printOrderReceiptWithPrinterConfigs(order, printerConfigs, type = 'new', options = {}) { + const key = `order:${order && order.id}:${type}`; + const cooldownMs = (options && typeof options.cooldownMs === 'number') ? options.cooldownMs : 30000; + return await this._withInflight(key, async () => { + try { + if (!printerConfigs || printerConfigs.length === 0) { + console.log('[PrinterManager] No printer configurations provided, falling back to legacy method'); + return await this.printOrderReceipt(order, type, options); + } + + console.log(`[PrinterManager] Printing order #${order.id} to ${printerConfigs.length} configured printer(s)`); + + const jobs = printerConfigs.map(async (config, idx) => { + console.log(`[PrinterManager] Print job ${idx + 1}/${printerConfigs.length} for order #${order.id}: ${config.name}`); + + if (config.type === 'system') { + await this.printSystemReceiptWithConfig(order, type, options, config); + } else { + const instance = this.createPrinterInstanceFromConfig(config); + await this.renderReceipt(instance, order, type, options, config); + + // Normalize interface for execution + let normalizedInterface = config.interface; + if (config.type === 'network') { + if (!normalizedInterface.startsWith('tcp://')) { + const hasPort = normalizedInterface.includes(':'); + normalizedInterface = hasPort ? `tcp://${normalizedInterface}` : `tcp://${normalizedInterface}:9100`; + } + } else if (config.type === 'com' || config.type === 'serial') { + if (/^COM\d+$/i.test(normalizedInterface)) { + normalizedInterface = `\\\\.\\${normalizedInterface.toUpperCase()}`; + } + } + + await this.executeForInstance(instance, normalizedInterface); + } + console.log(`[PrinterManager] Successfully printed to: ${config.name}`); + }); + + const results = await Promise.allSettled(jobs); + const succeeded = results.filter(r => r.status === 'fulfilled').length; + const failed = results.length - succeeded; + console.log(`[PrinterManager] Print results for order #${order.id}: ${succeeded} succeeded, ${failed} failed`); + + if (succeeded === 0) { + const firstErr = results.find(r => r.status === 'rejected'); + return { success: false, error: firstErr && firstErr.reason ? firstErr.reason.message || String(firstErr.reason) : 'All prints failed' }; + } + return { success: true }; + } catch (error) { + console.error('[PrinterManager] Print error:', error.message); + return { success: false, error: error.message }; + } + }, cooldownMs); + } + + // ===== System printer (Windows spooler) support via PDF rendering ===== + async printSystemTest(printerName) { + const filePath = path.join(os.tmpdir(), `kitchen-agent-test-${Date.now()}-${Math.random().toString(36).slice(2)}.pdf`); + try { + await this.writeTestPdfToPath(filePath); + await pdfPrinter.print(filePath, { printer: printerName }); + // Give the spooler a moment to open the file before deletion + await new Promise(resolve => setTimeout(resolve, 1500)); + } finally { + try { fs.unlinkSync(filePath); } catch (_) {} + } + } + + async printSystemReceipt(order, type, options, printerName) { + const filePath = path.join(os.tmpdir(), `kitchen-agent-receipt-${order && order.id ? order.id : 'x'}-${Date.now()}-${Math.random().toString(36).slice(2)}.pdf`); + try { + await this.writeReceiptPdfToPath(filePath, order, type, options); + await pdfPrinter.print(filePath, { printer: printerName }); + // Give the spooler a moment to open the file before deletion + await new Promise(resolve => setTimeout(resolve, 2000)); + } finally { + try { fs.unlinkSync(filePath); } catch (_) {} + } + } + + async printSystemReceiptWithConfig(order, type, options, printerConfig) { + const filePath = path.join(os.tmpdir(), `kitchen-agent-receipt-${order && order.id ? order.id : 'x'}-${Date.now()}-${Math.random().toString(36).slice(2)}.pdf`); + try { + await this.writeReceiptPdfToPathWithConfig(filePath, order, type, options, printerConfig); + await pdfPrinter.print(filePath, { printer: printerConfig.interface }); + // Give the spooler a moment to open the file before deletion + await new Promise(resolve => setTimeout(resolve, 2000)); + } finally { + try { fs.unlinkSync(filePath); } catch (_) {} + } + } + + async writeTestPdfToPath(filePath) { + const doc = new PDFDocument({ size: 'A4', margin: 36 }); + return new Promise((resolve, reject) => { + const stream = fs.createWriteStream(filePath); + doc.pipe(stream); + // Header + doc.fontSize(18).text('TEST PRINT', { align: 'center' }); + doc.moveDown(0.5); + doc.moveTo(36, doc.y).lineTo(559, doc.y).stroke(); + doc.moveDown(0.5); + doc.fontSize(12).text(`Printer Type: ${this.config && this.config.printerType || 'N/A'}`); + doc.text(`Interface: ${this.currentInterface || this.config && this.config.printerPath || 'N/A'}`); + doc.text(`Width: ${this.config && this.config.printerWidth || 'N/A'} chars`); + doc.text(`Time: ${new Date().toLocaleString()}`); + doc.moveDown(0.5); + doc.moveTo(36, doc.y).lineTo(559, doc.y).stroke(); + doc.moveDown(0.5); + doc.fontSize(14).text('Test Successful', { align: 'center' }); + doc.end(); + stream.on('finish', () => resolve(true)); + stream.on('error', reject); + }); + } + + async writeReceiptPdfToPath(filePath, order, type = 'new', options = {}) { + const cfg = this.config || {}; + const doc = new PDFDocument({ size: 'A4', margin: 36 }); + const maxWidth = 523; // 559 - 36 + return new Promise(async (resolve, reject) => { + const stream = fs.createWriteStream(filePath); + doc.pipe(stream); + + // Business header + doc.fontSize(18).text(cfg.businessName || 'KITCHEN ORDER', { align: 'center' }); + if (cfg.businessAddress || cfg.businessPhone || cfg.businessWebsite || cfg.businessEmail) { + doc.moveDown(0.2); + doc.fontSize((cfg.businessContactSize || 'normal') === 'large' ? 12 : 10); + [cfg.businessAddress, cfg.businessPhone, cfg.businessWebsite, cfg.businessEmail] + .filter(Boolean) + .forEach(line => doc.text(line, { align: 'center' })); + } + + if (cfg.headerText) { + doc.moveDown(0.4); + doc.fontSize(12).text(cfg.headerText, { align: 'center' }); + } + + doc.moveDown(0.4); + doc.moveTo(36, doc.y).lineTo(559, doc.y).stroke(); + + // Order banner + doc.moveDown(0.4); + doc.fontSize(14).text(type === 'canceled' ? '*** ORDER CANCELED ***' : 'NEW ORDER', { align: 'left' }); + if (type === 'canceled' && options && options.reason) { + doc.moveDown(0.2); + doc.fontSize(12).text(`Cancellation reason: ${String(options.reason)}`); + } + + // Order details + doc.moveDown(0.4); + doc.fontSize(12).text(`Order #${order.id}`); + if (cfg.showTimestamps === 'true' || !cfg.showTimestamps) { + doc.text(`Time: ${new Date(order.createdAt * 1000).toLocaleString()}`); + } + doc.text(`Type: ${order.order.type || 'N/A'}`); + + // Customer + if (cfg.showCustomerInfo === 'true' || !cfg.showCustomerInfo) { + doc.moveDown(0.4); + doc.fontSize(12).text('CUSTOMER:'); + doc.fontSize(11).text(order.customer.name || 'N/A'); + if (order.customer.phoneNumber) doc.text(order.customer.phoneNumber); + if (order.order.deliveryAddress) doc.text(`Address: ${order.order.deliveryAddress}`); + } + + // Items + if (order.order.items && Array.isArray(order.order.items) && (cfg.showOrderItems === 'true' || !cfg.showOrderItems)) { + doc.moveDown(0.4); + doc.fontSize(12).text('ITEMS:'); + doc.fontSize(11); + order.order.items.forEach(item => { + const itemName = item.itemName || item.name || 'Unknown Item'; + const qty = item.qty || 1; + const price = item.price || 0; + const line = (cfg.showPrices === 'true' || !cfg.showPrices) + ? `${qty}x ${itemName} - $${(price * qty).toFixed(2)}` + : `${qty}x ${itemName}`; + doc.text(line, { width: maxWidth }); + if (item.addons && Array.isArray(item.addons)) { + item.addons.forEach(addon => doc.text(` + ${addon.name || addon}`)); + } + if (item.exclude && Array.isArray(item.exclude)) { + item.exclude.forEach(ex => doc.text(` - NO ${ex.name || ex}`)); + } + }); + } + + if (order.order.specialInstructions) { + doc.moveDown(0.4); + doc.fontSize(12).text('SPECIAL INSTRUCTIONS:'); + doc.fontSize(11).text(String(order.order.specialInstructions), { width: maxWidth }); + } + + if (order.order.deliveryInstructions) { + doc.moveDown(0.4); + doc.fontSize(12).text('DELIVERY INSTRUCTIONS:'); + doc.fontSize(11).text(String(order.order.deliveryInstructions), { width: maxWidth }); + } + + if (order.order.foodAllergy) { + doc.moveDown(0.4); + doc.fontSize(12).text('FOOD ALLERGY WARNING', { align: 'center' }); + if (order.order.foodAllergyNotes) doc.fontSize(11).text(order.order.foodAllergyNotes, { align: 'center' }); + } + + if (cfg.showPrices === 'true' || !cfg.showPrices) { + doc.moveDown(0.4); + // Price breakdown (normal font, right-aligned) + try { + const rawSubtotal = (order && typeof order.amount !== 'undefined') + ? order.amount + : (order && order.order && typeof order.order.amount !== 'undefined' ? order.order.amount : null); + const rawTaxAmount = (order && typeof order.taxAmount !== 'undefined') + ? order.taxAmount + : (order && order.order && typeof order.order.taxAmount !== 'undefined' ? order.order.taxAmount : null); + const rawTaxRate = (order && typeof order.taxRate !== 'undefined') + ? order.taxRate + : (order && order.order && typeof order.order.taxRate !== 'undefined' ? order.order.taxRate : null); + const rawDelivery = (order && typeof order.deliveryFee !== 'undefined') + ? order.deliveryFee + : (order && order.order && typeof order.order.deliveryFee !== 'undefined' ? order.order.deliveryFee : null); + + if (typeof rawSubtotal === 'number' && isFinite(rawSubtotal)) { + doc.fontSize(12).text(`Subtotal: $${rawSubtotal.toFixed(2)}`, { align: 'right' }); + } + if (typeof rawTaxAmount === 'number' && isFinite(rawTaxAmount) && rawTaxAmount > 0) { + let taxLabel = 'Tax'; + if (typeof rawTaxRate === 'number' && isFinite(rawTaxRate) && rawTaxRate > 0) { + const rateStr = Number(rawTaxRate).toFixed(2).replace(/\.?0+$/, ''); + taxLabel = `Tax (${rateStr}%)`; + } + doc.fontSize(12).text(`${taxLabel}: $${rawTaxAmount.toFixed(2)}`, { align: 'right' }); + } + if (typeof rawDelivery === 'number' && isFinite(rawDelivery) && rawDelivery > 0) { + doc.fontSize(12).text(`Delivery Fee: $${rawDelivery.toFixed(2)}`, { align: 'right' }); + } + } catch (_) {} + // Keep TOTAL style unchanged + doc.fontSize(12).text(`TOTAL: $${(order.totalAmount || 0).toFixed(2)}`); + } + + if (cfg.footerText) { + doc.moveDown(0.6); + doc.fontSize(12).text(cfg.footerText, { align: 'center' }); + } + + doc.end(); + stream.on('finish', () => resolve(true)); + stream.on('error', reject); + }); + } + + async writeReceiptPdfToPathWithConfig(filePath, order, type = 'new', options = {}, config = {}) { + const doc = new PDFDocument({ size: 'A4', margin: 36 }); + const maxWidth = 523; // 559 - 36 + return new Promise(async (resolve, reject) => { + const stream = fs.createWriteStream(filePath); + doc.pipe(stream); + + // Business header + const businessName = config.business_name || config.businessName || 'KITCHEN ORDER'; + doc.fontSize(18).text(businessName, { align: 'center' }); + + const businessAddress = config.business_address || config.businessAddress; + const businessPhone = config.business_phone || config.businessPhone; + const businessWebsite = config.business_website || config.businessWebsite; + const businessEmail = config.business_email || config.businessEmail; + + if (businessAddress || businessPhone || businessWebsite || businessEmail) { + doc.moveDown(0.2); + const contactSize = config.business_contact_size || config.businessContactSize || 'normal'; + doc.fontSize(contactSize === 'large' ? 12 : 10); + [businessAddress, businessPhone, businessWebsite, businessEmail] + .filter(Boolean) + .forEach(line => doc.text(line, { align: 'center' })); + } + + const headerText = config.header_text || config.headerText; + if (headerText) { + doc.moveDown(0.4); + doc.fontSize(12).text(headerText, { align: 'center' }); + } + + doc.moveDown(0.4); + doc.moveTo(36, doc.y).lineTo(559, doc.y).stroke(); + + // Order banner + doc.moveDown(0.4); + doc.fontSize(14).text(type === 'canceled' ? '*** ORDER CANCELED ***' : 'NEW ORDER', { align: 'left' }); + if (type === 'canceled' && options && options.reason) { + doc.moveDown(0.2); + doc.fontSize(12).text(`Cancellation reason: ${String(options.reason)}`); + } + + // Order details + doc.moveDown(0.4); + doc.fontSize(12).text(`Order #${order.id}`); + // Merge global toggles for PDF path as well + const mergedShowTimestamps = (config.show_timestamps !== false) && (config.showTimestamps !== 'false'); + if (mergedShowTimestamps) { + doc.text(`Time: ${new Date(order.createdAt * 1000).toLocaleString()}`); + } + doc.text(`Type: ${order.order.type || 'N/A'}`); + + // Customer + const mergedShowCustomer = (config.show_customer_info !== false) && (config.showCustomerInfo !== 'false'); + if (mergedShowCustomer) { + doc.moveDown(0.4); + doc.fontSize(12).text('CUSTOMER:'); + doc.fontSize(11).text(order.customer.name || 'N/A'); + if (order.customer.phoneNumber) doc.text(order.customer.phoneNumber); + if (order.order.deliveryAddress) doc.text(`Address: ${order.order.deliveryAddress}`); + } + + // Items + const mergedShowItems = (config.show_order_items !== false) && (config.showOrderItems !== 'false'); + const mergedShowPrices = (config.show_prices !== false) && (config.showPrices !== 'false'); + if (order.order.items && Array.isArray(order.order.items) && mergedShowItems) { + doc.moveDown(0.4); + doc.fontSize(12).text('ITEMS:'); + doc.fontSize(11); + order.order.items.forEach(item => { + const itemName = item.itemName || item.name || 'Unknown Item'; + const qty = item.qty || 1; + const price = item.price || 0; + const line = mergedShowPrices + ? `${qty}x ${itemName} - $${(price * qty).toFixed(2)}` + : `${qty}x ${itemName}`; + doc.text(line, { width: maxWidth }); + if (item.addons && Array.isArray(item.addons)) { + item.addons.forEach(addon => doc.text(` + ${addon.name || addon}`)); + } + if (item.exclude && Array.isArray(item.exclude)) { + item.exclude.forEach(ex => doc.text(` - NO ${ex.name || ex}`)); + } + }); + } + + if (order.order.specialInstructions) { + doc.moveDown(0.4); + doc.fontSize(12).text('SPECIAL INSTRUCTIONS:'); + doc.fontSize(11).text(String(order.order.specialInstructions), { width: maxWidth }); + } + + if (order.order.deliveryInstructions) { + doc.moveDown(0.4); + doc.fontSize(12).text('DELIVERY INSTRUCTIONS:'); + doc.fontSize(11).text(String(order.order.deliveryInstructions), { width: maxWidth }); + } + + if (order.order.foodAllergy) { + doc.moveDown(0.4); + doc.fontSize(12).text('FOOD ALLERGY WARNING', { align: 'center' }); + if (order.order.foodAllergyNotes) doc.fontSize(11).text(order.order.foodAllergyNotes, { align: 'center' }); + } + + if (mergedShowPrices) { + doc.moveDown(0.4); + // Price breakdown (normal font, right-aligned) + try { + const rawSubtotal = (order && typeof order.amount !== 'undefined') + ? order.amount + : (order && order.order && typeof order.order.amount !== 'undefined' ? order.order.amount : null); + const rawTaxAmount = (order && typeof order.taxAmount !== 'undefined') + ? order.taxAmount + : (order && order.order && typeof order.order.taxAmount !== 'undefined' ? order.order.taxAmount : null); + const rawTaxRate = (order && typeof order.taxRate !== 'undefined') + ? order.taxRate + : (order && order.order && typeof order.order.taxRate !== 'undefined' ? order.order.taxRate : null); + const rawDelivery = (order && typeof order.deliveryFee !== 'undefined') + ? order.deliveryFee + : (order && order.order && typeof order.order.deliveryFee !== 'undefined' ? order.order.deliveryFee : null); + + if (typeof rawSubtotal === 'number' && isFinite(rawSubtotal)) { + doc.fontSize(12).text(`Subtotal: $${rawSubtotal.toFixed(2)}`, { align: 'right' }); + } + if (typeof rawTaxAmount === 'number' && isFinite(rawTaxAmount) && rawTaxAmount > 0) { + let taxLabel = 'Tax'; + if (typeof rawTaxRate === 'number' && isFinite(rawTaxRate) && rawTaxRate > 0) { + const rateStr = Number(rawTaxRate).toFixed(2).replace(/\.?0+$/, ''); + taxLabel = `Tax (${rateStr}%)`; + } + doc.fontSize(12).text(`${taxLabel}: $${rawTaxAmount.toFixed(2)}`, { align: 'right' }); + } + if (typeof rawDelivery === 'number' && isFinite(rawDelivery) && rawDelivery > 0) { + doc.fontSize(12).text(`Delivery Fee: $${rawDelivery.toFixed(2)}`, { align: 'right' }); + } + } catch (_) {} + // Keep TOTAL style unchanged + doc.fontSize(12).text(`TOTAL: $${(order.totalAmount || 0).toFixed(2)}`); + } + + const footerText = config.footer_text || config.footerText; + if (footerText) { + doc.moveDown(0.6); + doc.fontSize(12).text(footerText, { align: 'center' }); + } + + doc.end(); + stream.on('finish', () => resolve(true)); + stream.on('error', reject); + }); + } + + isConnected() { + // Check if printer interface is accessible + if (!this.config || !this.currentInterface) { + return false; + } + + // For file-based interfaces (USB, serial), check if file exists + if (this.config.printerInterface === 'usb') { + return fs.existsSync(this.currentInterface); + } + // For serial COM on Windows, SerialPort.list() will detect; assume connected and rely on write errors + if (this.config.printerInterface === 'serial') { + return true; + } + + // For network printers, assume connected (will fail on execute if not) + return true; + } + + // Lightweight reachability check for a specific normalized interface + async isTargetReachable(target) { + try { + if (!target || !target.type || !target.interface) return false; + const t = String(target.type).toLowerCase(); + const iface = String(target.interface); + if (t === 'usb') { + return fs.existsSync(iface); + } + if (t === 'com' || t === 'serial') { + // Assume reachable; OS/driver may hold the port and our open test would be unreliable. + // Actual execute will surface connectivity errors which the worker handles by retrying later. + return true; + } + if (t === 'network') { + // Attempt TCP connection quickly (Node net not used here to avoid extra deps). Rely on library on execute. + return true; + } + if (t === 'system') { + // Assume system spooler availability; printing will surface errors. + return true; + } + return false; + } catch (_) { + return false; + } + } + + // Determine if any configured printers are likely reachable now + async anyConfiguredPrinterReachable(printerConfigs) { + try { + if (Array.isArray(printerConfigs) && printerConfigs.length > 0) { + for (const cfg of printerConfigs) { + let normalizedInterface = cfg.interface; + let type = cfg.type; + if (type === 'network') { + if (!normalizedInterface.startsWith('tcp://')) { + const hasPort = normalizedInterface.includes(':'); + normalizedInterface = hasPort ? `tcp://${normalizedInterface}` : `tcp://${normalizedInterface}:9100`; + } + } else if (type === 'com' || type === 'serial') { + if (/^COM\d+$/i.test(normalizedInterface)) { + normalizedInterface = `\\\\.\\${normalizedInterface.toUpperCase()}`; + type = 'com'; + } + } + const reachable = await this.isTargetReachable({ type, interface: normalizedInterface }); + if (reachable) return true; + } + return false; + } + // Also consider selected printers from config (selectedPrintersJson) + const selectedTargets = this.getSelectedTargets(); + if (Array.isArray(selectedTargets) && selectedTargets.length > 0) { + for (const t of selectedTargets) { + const reachable = await this.isTargetReachable(t); + if (reachable) return true; + } + return false; + } + // Fallback to single configured printer + const main = this.getMainPrinterTarget(); + if (!main) return false; + return await this.isTargetReachable(main); + } catch (_) { + return false; + } + } + + async getAvailablePrinters() { + try { + const all = []; + + // Windows system printers via pdf-to-printer + try { + const printers = await pdfPrinter.getPrinters(); + printers.forEach(p => { + const sysName = p && (p.name || p.deviceId) ? (p.name || p.deviceId) : String(p); + all.push({ + name: sysName, + type: 'system', + interface: sysName, + isDefault: !!(p && p.isDefault), + status: 'Available' + }); + }); + } catch (err) { + console.error('List system printers failed:', err.message); + } + + // Serial/COM ports + try { + const ports = await SerialPort.list(); + ports.forEach(port => { + all.push({ + name: `${port.path} ${port.manufacturer ? '(' + port.manufacturer + ')' : ''}`.trim(), + type: 'com', + interface: port.path, + isDefault: false, + status: 'Available', + details: { + manufacturer: port.manufacturer, + serialNumber: port.serialNumber, + vendorId: port.vendorId, + productId: port.productId + } + }); + }); + } catch (err) { + console.error('List COM ports failed:', err.message); + } + + return all; + } catch (error) { + console.error('getAvailablePrinters error:', error.message); + return []; + } + } +} + +module.exports = new PrinterManager(); + diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..a62e43e --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,1604 @@ +/* Reset and Base Styles */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 16px; + line-height: 1.5; + color: #333; + background-color: #f5f5f5; +} + +/* Touch-friendly tap targets */ +button, a, input, select, textarea { + min-height: 44px; + min-width: 44px; +} + +/* Login Page */ +.login-page { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +} + +.login-container { + width: 100%; + max-width: 400px; + padding: 20px; +} + +.login-card { + background: white; + border-radius: 8px; + padding: 40px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); +} + +.login-card h1 { + font-size: 28px; + margin-bottom: 10px; + text-align: center; + color: #667eea; +} + +.login-card h2 { + font-size: 20px; + margin-bottom: 30px; + text-align: center; + color: #666; +} + +.login-footer { + margin-top: 30px; + text-align: center; + font-size: 14px; + color: #999; +} + +/* Forms */ +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: #333; +} + +.form-group input[type="text"], +.form-group input[type="email"], +.form-group input[type="password"], +.form-group input[type="number"], +.form-group select, +.form-group textarea { + width: 100%; + padding: 12px 16px; + border: 2px solid #ddd; + border-radius: 4px; + font-size: 16px; + transition: border-color 0.3s; +} + +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: #667eea; +} + +.form-group small { + display: block; + margin-top: 5px; + font-size: 13px; + color: #666; +} + +.checkbox-label { + display: flex; + align-items: center; + cursor: pointer; + font-weight: normal; +} + +.checkbox-label input[type="checkbox"] { + margin-right: 10px; + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; +} + +/* Buttons */ +.btn { + display: inline-block; + padding: 12px 24px; + font-size: 16px; + font-weight: 600; + text-align: center; + text-decoration: none; + border: none; + border-radius: 4px; + cursor: pointer; + transition: all 0.3s; + min-height: 48px; +} + +.btn-primary { + background-color: #667eea; + color: white; +} + +.btn-primary:hover { + background-color: #5568d3; +} + +.btn-secondary { + background-color: #6c757d; + color: white; +} + +.btn-secondary:hover { + background-color: #5a6268; +} + +.btn-success { + background-color: #28a745; + color: white; +} + +.btn-success:hover { + background-color: #218838; +} + +.btn-danger { + background-color: #dc3545; + color: white; +} + +.btn-danger:hover { + background-color: #c82333; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-large { + padding: 16px 32px; + font-size: 18px; +} + +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Alerts */ +.alert { + padding: 16px; + border-radius: 4px; + margin-bottom: 20px; +} + +.alert-success { + background-color: #d4edda; + border: 1px solid #c3e6cb; + color: #155724; +} + +.alert-error { + background-color: #f8d7da; + border: 1px solid #f5c6cb; + color: #721c24; +} + +.alert-info { + background-color: #d1ecf1; + border: 1px solid #bee5eb; + color: #0c5460; +} + +/* Header */ +.main-header { + background-color: #fff; + border-bottom: 2px solid #e0e0e0; + padding: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.header-content { + max-width: 1400px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 15px; +} + +.header-content h1 { + font-size: 28px; + color: #333; +} + +.header-actions { + display: flex; + gap: 10px; +} + +/* Connection Status Bar */ +.connection-status-bar { + display: flex; + gap: 20px; + align-items: center; + padding: 8px 0; +} + +.connection-status-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 12px; + background-color: #f8f9fa; + border-radius: 6px; + border: 1px solid #e0e0e0; + transition: all 0.3s ease; + cursor: help; + min-height: auto; +} + +.connection-status-item:hover { + background-color: #e9ecef; +} + +.status-indicator { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; + position: relative; + transition: all 0.3s ease; +} + +/* Status indicator animations and colors */ +.status-indicator.status-online { + background-color: #28a745; + box-shadow: 0 0 8px rgba(40, 167, 69, 0.6); +} + +.status-indicator.status-offline { + background-color: #dc3545; + box-shadow: 0 0 8px rgba(220, 53, 69, 0.6); +} + +.status-indicator.status-checking { + background-color: #ffc107; + box-shadow: 0 0 8px rgba(255, 193, 7, 0.6); + animation: pulse 1.5s infinite; +} + +.status-indicator.status-unconfigured { + background-color: #6c757d; + box-shadow: 0 0 8px rgba(108, 117, 125, 0.4); +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.status-text { + font-size: 13px; + font-weight: 600; + color: #333; +} + +.status-label { + font-size: 11px; + color: #666; + text-transform: uppercase; + font-weight: 500; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .connection-status-bar { + width: 100%; + order: 3; + justify-content: center; + } + + .header-content h1 { + order: 1; + } + + .header-actions { + order: 2; + } +} + +/* Stats Bar */ +.stats-bar { + background-color: #fff; + padding: 20px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + max-width: 1400px; + margin: 20px auto; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.stat-card { + padding: 20px; + text-align: center; + border-radius: 8px; + background-color: #f8f9fa; + border: 2px solid #e0e0e0; +} + +.stat-card.stat-new { + background-color: #d4edda; + border-color: #28a745; +} + +.stat-card.stat-preparing { + background-color: #fff3cd; + border-color: #ffc107; +} + +.stat-card.stat-ready { + background-color: #d1ecf1; + border-color: #17a2b8; +} + +.stat-label { + font-size: 14px; + color: #666; + font-weight: 600; + text-transform: uppercase; + margin-bottom: 8px; +} + +.stat-value { + font-size: 36px; + font-weight: bold; + color: #333; +} + +/* Dashboard Controls */ +.dashboard-controls { + max-width: 1400px; + margin: 0 auto 20px; + padding: 0 20px; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 15px; +} + +.filter-buttons { + display: flex; + gap: 10px; + flex-wrap: wrap; +} + +.filter-btn { + padding: 12px 24px; + border: 2px solid #667eea; + background-color: white; + color: #667eea; + border-radius: 4px; + cursor: pointer; + font-weight: 600; + font-size: 16px; + transition: all 0.3s; +} + +.filter-btn:hover { + background-color: #f0f0f0; +} + +.filter-btn.active { + background-color: #667eea; + color: white; +} + +/* Sync Button with Integrated Loading State */ +.sync-button { + position: relative; + display: inline-flex !important; + align-items: center; + justify-content: center; + gap: 8px; + width: 140px !important; + height: 48px !important; + min-height: 48px !important; + max-height: 48px !important; + padding: 0 20px !important; + transition: opacity 0.3s ease, background-color 0.3s ease !important; + white-space: nowrap; + overflow: hidden; + box-sizing: border-box; +} + +.sync-spinner { + display: none; + width: 16px; + height: 16px; + border: 2.5px solid rgba(255, 255, 255, 0.3); + border-top-color: #ffffff; + border-radius: 50%; + animation: spin 0.8s linear infinite; + flex-shrink: 0; + margin: 0; +} + +.sync-button.loading .sync-spinner { + display: block; +} + +.sync-button.loading { + opacity: 0.8; + cursor: not-allowed; + pointer-events: none; +} + +.sync-text { + display: inline-block; + line-height: 1; + transition: opacity 0.3s ease; + flex-shrink: 0; + font-size: 16px; +} + +.sync-button.loading .sync-text { + opacity: 0.9; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Legacy refresh indicator - kept for backward compatibility */ +.refresh-indicator { + display: none; + align-items: center; + gap: 10px; + color: #666; + font-size: 14px; +} + +.refresh-indicator.visible { + display: flex; +} + +.spinner { + width: 20px; + height: 20px; + border: 3px solid #f3f3f3; + border-top: 3px solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Orders Container */ +.orders-container { + max-width: 1400px; + margin: 0 auto; + padding: 0 20px 40px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 20px; +} + +.loading { + grid-column: 1 / -1; + text-align: center; + padding: 60px; + font-size: 18px; + color: #666; +} + +.no-orders { + grid-column: 1 / -1; + text-align: center; + padding: 60px; + font-size: 18px; + color: #999; +} + +/* Order Card */ +.order-card { + background-color: white; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border-left: 5px solid #ccc; + transition: transform 0.2s, box-shadow 0.2s; + display: flex; + flex-direction: column; +} + +.order-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.order-card.status-new { + border-left-color: #28a745; +} + +.order-card.status-preparing { + border-left-color: #ffc107; +} + +.order-card.status-ready { + border-left-color: #17a2b8; +} + +.order-card.status-canceled { + border-left-color: #dc3545; + opacity: 0.7; +} + +.order-header { + display: flex; + justify-content: space-between; + align-items: start; + margin-bottom: 15px; +} + +.order-number { + font-size: 28px; + font-weight: bold; + color: #333; +} + +.order-badge { + padding: 6px 12px; + border-radius: 4px; + font-size: 13px; + font-weight: 600; + text-transform: uppercase; +} + +.order-badge.badge-new { + background-color: #28a745; + color: white; +} + +.order-badge.badge-preparing { + background-color: #ffc107; + color: #333; +} + +.order-badge.badge-ready { + background-color: #17a2b8; + color: white; +} + +.order-info { + margin-bottom: 15px; +} + +.order-info-row { + display: flex; + justify-content: space-between; + margin-bottom: 8px; + font-size: 15px; +} + +.order-info-label { + font-weight: 600; + color: #666; +} + +.order-info-value { + color: #333; +} + +.order-type-icon { + margin-right: 5px; +} + +.order-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 0; +} + +.order-actions .btn { + flex: 1; + min-width: 100px; + padding: 10px; + font-size: 14px; +} + +.order-details-toggle { + margin-top: 10px; + text-align: center; +} + +.order-details-toggle button { + background: none; + border: none; + color: #667eea; + cursor: pointer; + text-decoration: underline; + font-size: 14px; + padding: 5px; +} + +/* Settings Page */ +.settings-container { + max-width: 900px; + margin: 20px auto; + padding: 0 20px 40px; +} + +.tabs { + display: flex; + gap: 10px; + margin-bottom: 30px; + border-bottom: 2px solid #e0e0e0; +} + +.tab-btn { + padding: 15px 30px; + background: none; + border: none; + border-bottom: 3px solid transparent; + cursor: pointer; + font-size: 16px; + font-weight: 600; + color: #666; + transition: all 0.3s; +} + +.tab-btn:hover { + color: #333; +} + +.tab-btn.active { + color: #667eea; + border-bottom-color: #667eea; +} + +.tab-content { + display: none; + background: white; + padding: 30px; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.tab-content.active { + display: block; +} + +/* Settings Sections */ +.settings-section { + margin-bottom: 35px; + padding-bottom: 25px; + border-bottom: 1px solid #e0e0e0; +} + +.settings-section:last-child { + border-bottom: none; + margin-bottom: 0; +} + +.settings-section h3 { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 8px; +} + +.section-description { + font-size: 14px; + color: #666; + margin-bottom: 20px; + line-height: 1.6; +} + +.tab-content h2 { + font-size: 24px; + margin-bottom: 25px; + color: #333; +} + +.tab-content h3 { + font-size: 18px; + margin: 25px 0 15px; + color: #555; +} + +.form-actions { + margin-top: 30px; + padding-top: 30px; + border-top: 2px solid #e0e0e0; + text-align: center; +} + +/* Modal */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + z-index: 1000; + align-items: center; + justify-content: center; + animation: fadeIn 0.2s ease; +} + +.modal.visible { + display: flex; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideUp { + from { + transform: translateY(30px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.modal-content { + background-color: white; + padding: 30px; + border-radius: 8px; + max-width: 500px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + animation: slideUp 0.3s ease; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); +} + +.modal-large { + max-width: 800px; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.modal-header h2 { + margin: 0; +} + +.close-btn { + background: none; + border: none; + font-size: 28px; + cursor: pointer; + color: #999; + padding: 0; + width: 40px; + height: 40px; +} + +.close-btn:hover { + color: #333; +} + +.modal-actions { + display: flex; + gap: 10px; + margin-top: 20px; + justify-content: flex-end; +} + +/* Status Change Confirmation Modal */ +.status-modal-content { + text-align: center; + max-width: 480px; + padding: 35px 40px; +} + +.status-modal-icon { + width: 80px; + height: 80px; + margin: 0 auto 20px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 40px; + animation: scaleIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +@keyframes scaleIn { + from { + transform: scale(0); + } + to { + transform: scale(1); + } +} + +.status-modal-icon.preparing { + background: linear-gradient(135deg, #fff3cd, #ffc107); + box-shadow: 0 4px 20px rgba(255, 193, 7, 0.4); +} + +.status-modal-icon.ready { + background: linear-gradient(135deg, #d1ecf1, #17a2b8); + box-shadow: 0 4px 20px rgba(23, 162, 184, 0.4); +} + +.status-modal-icon.completed { + background: linear-gradient(135deg, #d4edda, #28a745); + box-shadow: 0 4px 20px rgba(40, 167, 69, 0.4); +} + +#statusModalTitle { + font-size: 24px; + font-weight: 700; + color: #333; + margin-bottom: 25px; +} + +.status-modal-details { + background: #f8f9fa; + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + text-align: left; +} + +.status-modal-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid #e0e0e0; +} + +.status-modal-row:last-child { + border-bottom: none; +} + +.status-transition-row { + padding: 15px 0 8px; + justify-content: center; +} + +.status-modal-label { + font-weight: 600; + color: #666; + font-size: 14px; +} + +.status-modal-value { + font-weight: 500; + color: #333; + font-size: 15px; +} + +.status-transition { + display: flex; + align-items: center; + gap: 12px; + font-size: 16px; +} + +.status-badge { + padding: 6px 14px; + border-radius: 20px; + font-weight: 600; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.status-badge.badge-new { + background-color: #28a745; + color: white; +} + +.status-badge.badge-preparing { + background-color: #ffc107; + color: #333; +} + +.status-badge.badge-ready { + background-color: #17a2b8; + color: white; +} + +.status-badge.badge-completed { + background-color: #6c757d; + color: white; +} + +.status-arrow { + font-size: 20px; + color: #667eea; + font-weight: bold; + animation: slideRight 1s ease infinite; +} + +@keyframes slideRight { + 0%, 100% { + transform: translateX(0); + } + 50% { + transform: translateX(5px); + } +} + +.status-modal-message { + font-size: 15px; + color: #666; + margin-bottom: 20px; + line-height: 1.5; +} + +.status-confirm-btn { + min-width: 120px; + font-weight: 700; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.status-confirm-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4); +} + +.status-confirm-btn:active { + transform: translateY(0); +} + +/* Toast Notification */ +.toast { + position: fixed; + bottom: 30px; + right: 30px; + background-color: #333; + color: white; + padding: 16px 24px; + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + z-index: 2000; + opacity: 0; + transform: translateY(20px); + transition: all 0.3s; + pointer-events: none; +} + +.toast.visible { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +.toast.success { + background-color: #28a745; +} + +.toast.error { + background-color: #dc3545; +} + +/* Responsive */ +@media (max-width: 768px) { + .orders-container { + grid-template-columns: 1fr; + } + + .stats-bar { + grid-template-columns: repeat(2, 1fr); + } + + .header-content { + flex-direction: column; + align-items: flex-start; + gap: 15px; + } + + .dashboard-controls { + flex-direction: column; + align-items: stretch; + } + + .filter-buttons { + justify-content: center; + } + + .tabs { + flex-direction: column; + } + + .tab-btn { + border-bottom: none; + border-left: 3px solid transparent; + } + + .tab-btn.active { + border-left-color: #667eea; + } +} + +@media (max-width: 480px) { + .stats-bar { + grid-template-columns: 1fr; + } + + .order-actions { + flex-direction: column; + } + + .order-actions .btn { + width: 100%; + } + + .status-modal-content { + padding: 25px 20px; + } + + .status-modal-icon { + width: 70px; + height: 70px; + font-size: 35px; + } + + #statusModalTitle { + font-size: 20px; + } + + .status-transition { + flex-direction: column; + gap: 8px; + } + + .status-arrow { + transform: rotate(90deg); + animation: slideDown 1s ease infinite; + } + + @keyframes slideDown { + 0%, 100% { + transform: rotate(90deg) translateX(0); + } + 50% { + transform: rotate(90deg) translateX(5px); + } + } + + .modal-actions { + flex-direction: column-reverse; + } + + .modal-actions .btn { + width: 100%; + } +} + +/* Order Details */ +.detail-section { + margin-bottom: 20px; + padding-bottom: 15px; + border-bottom: 1px solid #e0e0e0; +} + +.detail-section:last-child { + border-bottom: none; +} + +.detail-section h3 { + font-size: 16px; + font-weight: 600; + margin-bottom: 10px; + color: #333; +} + +.detail-section p { + margin: 5px 0; + color: #666; +} + +.detail-section table { + font-size: 14px; +} + +.detail-section table th { + font-weight: 600; + color: #333; +} + +.current-logo { + margin-top: 10px; + padding: 10px; + background-color: #f8f9fa; + border-radius: 4px; +} + +.current-logo p { + margin: 0; + font-size: 14px; + color: #666; +} + +/* Printer Management Styles */ +.printer-cards-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(450px, 1fr)); + gap: 20px; + margin-top: 20px; +} + +.printer-card { + background: white; + border: 2px solid #e0e0e0; + border-radius: 8px; + padding: 20px; + transition: all 0.3s; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.printer-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.printer-card.disabled { + opacity: 0.6; + background-color: #f5f5f5; +} + +.printer-card-header { + display: flex; + justify-content: space-between; + align-items: start; + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 2px solid #f0f0f0; +} + +.printer-card-header h4 { + margin: 0 0 8px 0; + font-size: 18px; + color: #333; + font-weight: 600; +} + +.printer-badges { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.printer-badge { + display: inline-block; + padding: 4px 10px; + border-radius: 4px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.printer-badge.badge-default { + background-color: #667eea; + color: white; +} + +.printer-badge.badge-enabled { + background-color: #28a745; + color: white; +} + +.printer-badge.badge-disabled { + background-color: #6c757d; + color: white; +} + +.printer-card-body { + margin-bottom: 15px; +} + +.printer-info-row { + display: flex; + justify-content: space-between; + margin-bottom: 8px; + font-size: 14px; +} + +.printer-info-label { + font-weight: 600; + color: #666; + min-width: 100px; +} + +.printer-info-value { + color: #333; + text-align: right; + word-break: break-word; +} + +.printer-card-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #f0f0f0; +} + +.btn-sm { + padding: 8px 14px; + font-size: 13px; + min-height: 36px; +} + +.no-printers { + grid-column: 1 / -1; + text-align: center; + padding: 60px 20px; + background: white; + border: 2px dashed #ddd; + border-radius: 8px; + color: #999; +} + +.no-printers p { + margin: 10px 0; + font-size: 16px; +} + +/* Printer Modal Styles */ +.printer-modal-body { + max-height: 70vh; + overflow-y: auto; +} + +.printer-modal-tabs { + display: flex; + gap: 5px; + margin-bottom: 25px; + border-bottom: 2px solid #e0e0e0; + flex-wrap: wrap; +} + +.printer-modal-tab-btn { + padding: 12px 20px; + background: none; + border: none; + border-bottom: 3px solid transparent; + cursor: pointer; + font-size: 14px; + font-weight: 600; + color: #666; + transition: all 0.3s; +} + +.printer-modal-tab-btn:hover { + color: #333; + background-color: #f8f9fa; +} + +.printer-modal-tab-btn.active { + color: #667eea; + border-bottom-color: #667eea; +} + +.printer-modal-tab-content { + display: none; +} + +.printer-modal-tab-content.active { + display: block; +} + +.detected-printer-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + margin-bottom: 8px; + background-color: #f8f9fa; + border-radius: 4px; + border: 1px solid #e0e0e0; +} + +.detected-printer-item span { + flex: 1; + font-size: 14px; +} + +/* Responsive adjustments for printer management */ +@media (max-width: 768px) { + .printer-cards-container { + grid-template-columns: 1fr; + } + + .printer-modal-tabs { + flex-direction: column; + gap: 0; + } + + .printer-modal-tab-btn { + border-bottom: none; + border-left: 3px solid transparent; + text-align: left; + } + + .printer-modal-tab-btn.active { + border-left-color: #667eea; + } + + .printer-card-actions { + flex-direction: column; + } + + .printer-card-actions .btn { + width: 100%; + } +} + +/* Kitchen Display - Enhanced Order Cards */ +.allergy-warning { + display: flex; + align-items: start; + gap: 12px; + background-color: #fff3cd; + border: 3px solid #ff6b6b; + border-radius: 8px; + padding: 15px; + margin-bottom: 15px; + animation: pulse-border 2s ease-in-out infinite; +} + +@keyframes pulse-border { + 0%, 100% { + border-color: #ff6b6b; + } + 50% { + border-color: #ff4444; + } +} + +.allergy-icon { + font-size: 28px; + line-height: 1; + flex-shrink: 0; +} + +.allergy-content { + flex: 1; +} + +.allergy-title { + font-size: 16px; + font-weight: 700; + color: #d32f2f; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 4px; +} + +.allergy-notes { + font-size: 15px; + font-weight: 600; + color: #333; + line-height: 1.4; +} + +.order-items-section { + background-color: #f8f9fa; + border-radius: 6px; + padding: 15px; + margin-bottom: 15px; +} + +.order-items-title { + font-size: 15px; + font-weight: 700; + color: #333; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 12px; + padding-bottom: 8px; + border-bottom: 2px solid #dee2e6; +} + +.order-item { + background-color: white; + border-radius: 6px; + padding: 12px; + margin-bottom: 10px; + border-left: 4px solid #667eea; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.order-item:last-child { + margin-bottom: 0; +} + +.item-qty-name { + display: flex; + align-items: baseline; + gap: 10px; + margin-bottom: 6px; +} + +.item-qty { + font-size: 18px; + font-weight: 700; + color: #667eea; + min-width: 40px; +} + +.item-name { + font-size: 16px; + font-weight: 600; + color: #333; + line-height: 1.3; +} + +.item-modifier { + font-size: 14px; + padding: 6px 10px; + margin-top: 6px; + border-radius: 4px; + line-height: 1.3; +} + +.item-addon { + background-color: #d4edda; + border-left: 3px solid #28a745; + color: #155724; + font-weight: 600; +} + +.item-exclude { + background-color: #f8d7da; + border-left: 3px solid #dc3545; + color: #721c24; + font-weight: 600; +} + +.special-instructions { + background-color: #e7f3ff; + border-left: 4px solid #2196F3; + border-radius: 6px; + padding: 12px 15px; + margin-bottom: 15px; +} + +.special-instructions-label { + font-size: 14px; + font-weight: 700; + color: #1565c0; + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.special-instructions-text { + font-size: 15px; + color: #333; + line-height: 1.5; + font-weight: 500; +} + +.order-total { + display: flex; + justify-content: space-between; + align-items: center; + background-color: #f8f9fa; + border-radius: 6px; + padding: 12px 15px; + margin-top: auto; + margin-bottom: 15px; + border: 2px solid #e0e0e0; +} + +.order-total-label { + font-size: 16px; + font-weight: 700; + color: #666; + text-transform: uppercase; +} + +.order-total-value { + font-size: 22px; + font-weight: 700; + color: #333; +} + +/* Responsive adjustments for kitchen display */ +@media (max-width: 480px) { + .allergy-warning { + padding: 12px; + } + + .allergy-icon { + font-size: 24px; + } + + .allergy-title { + font-size: 14px; + } + + .allergy-notes { + font-size: 14px; + } + + .order-items-section { + padding: 12px; + } + + .order-item { + padding: 10px; + } + + .item-qty { + font-size: 16px; + min-width: 35px; + } + + .item-name { + font-size: 15px; + } + + .item-modifier { + font-size: 13px; + padding: 5px 8px; + } + + .special-instructions { + padding: 10px 12px; + } + + .special-instructions-text { + font-size: 14px; + } + + .order-total-value { + font-size: 20px; + } +} + diff --git a/public/images/.gitkeep b/public/images/.gitkeep new file mode 100644 index 0000000..d5b0f57 --- /dev/null +++ b/public/images/.gitkeep @@ -0,0 +1,2 @@ +# This file ensures the images directory is created + diff --git a/public/js/common.js b/public/js/common.js new file mode 100644 index 0000000..874fc63 --- /dev/null +++ b/public/js/common.js @@ -0,0 +1,59 @@ +// Common utilities and functions + +function showToast(message, type = 'success') { + const toast = document.getElementById('toast'); + if (!toast) return; + + toast.textContent = message; + toast.className = 'toast visible ' + type; + + setTimeout(() => { + toast.classList.remove('visible'); + }, 3000); +} + +function formatTime(timestamp) { + const date = new Date(timestamp * 1000); + return date.toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit' + }); +} + +function formatDateTime(timestamp) { + const date = new Date(timestamp * 1000); + return date.toLocaleString('en-US', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +} + +function formatCurrency(amount) { + return '$' + (amount || 0).toFixed(2); +} + +// Tab switching functionality +document.addEventListener('DOMContentLoaded', function() { + const tabButtons = document.querySelectorAll('.tab-btn'); + const tabContents = document.querySelectorAll('.tab-content'); + + tabButtons.forEach(button => { + button.addEventListener('click', function() { + const targetTab = this.dataset.tab; + + // Remove active class from all buttons and contents + tabButtons.forEach(btn => btn.classList.remove('active')); + tabContents.forEach(content => content.classList.remove('active')); + + // Add active class to clicked button and corresponding content + this.classList.add('active'); + const targetContent = document.getElementById(targetTab + '-tab'); + if (targetContent) { + targetContent.classList.add('active'); + } + }); + }); +}); + diff --git a/public/js/dashboard.js b/public/js/dashboard.js new file mode 100644 index 0000000..41225f6 --- /dev/null +++ b/public/js/dashboard.js @@ -0,0 +1,953 @@ +// Dashboard functionality + +let currentFilter = 'all'; +let currentOrderIdForCancel = null; +let pendingStatusChange = { + orderId: null, + newStatus: null, + currentStatus: null, + orderData: null +}; + +// Track orders for notification detection +let previousOrderIds = new Set(); +let previousCanceledOrderIds = new Set(); +let isFirstLoad = true; + +// Connection status tracking +const connectionStatus = { + local: { + status: 'checking', + lastCheck: null, + consecutiveFailures: 0 + }, + api: { + status: 'checking', + lastCheck: null, + consecutiveFailures: 0, + responseTime: null + } +}; + +// Audio notification system +const audioNotification = { + newOrderSound: null, + canceledOrderSound: null, + enabled: true, + + init: function() { + // Load notification settings from server + fetch('/api/notification-settings') + .then(response => response.json()) + .then(data => { + if (!data.error) { + this.enabled = data.soundNotificationsEnabled !== 'false'; + const newOrderPath = data.newOrderSoundPath || '/public/sounds/new-order-notification.mp3'; + const canceledOrderPath = data.canceledOrderSoundPath || '/public/sounds/new-order-notification.mp3'; + + // Preload audio files + this.newOrderSound = new Audio(newOrderPath); + this.canceledOrderSound = new Audio(canceledOrderPath); + + // Set volume if specified + if (data.soundVolume) { + const volume = parseInt(data.soundVolume, 10) / 100; + this.newOrderSound.volume = volume; + this.canceledOrderSound.volume = volume; + } + } + }) + .catch(error => { + console.warn('Failed to load notification settings:', error); + // Use default sound + this.newOrderSound = new Audio('/public/sounds/new-order-notification.mp3'); + this.canceledOrderSound = new Audio('/public/sounds/new-order-notification.mp3'); + }); + }, + + playNewOrderSound: function() { + if (this.enabled && this.newOrderSound) { + this.newOrderSound.currentTime = 0; + this.newOrderSound.play().catch(error => { + console.warn('Failed to play new order sound:', error); + }); + } + }, + + playCanceledOrderSound: function() { + if (this.enabled && this.canceledOrderSound) { + this.canceledOrderSound.currentTime = 0; + this.canceledOrderSound.play().catch(error => { + console.warn('Failed to play canceled order sound:', error); + }); + } + } +}; + +// Initialize dashboard +document.addEventListener('DOMContentLoaded', function() { + setupFilterButtons(); + audioNotification.init(); + refreshOrders(); + + // Set up auto-refresh + setInterval(refreshOrders, config.dashboardRefreshInterval || 10000); + + // Initialize connection monitoring + checkConnectionStatus(); + // Check connection status every 15 seconds + setInterval(checkConnectionStatus, 15000); +}); + +function setupFilterButtons() { + const filterButtons = document.querySelectorAll('.filter-btn'); + + filterButtons.forEach(button => { + button.addEventListener('click', function() { + // Remove active class from all buttons + filterButtons.forEach(btn => btn.classList.remove('active')); + + // Add active class to clicked button + this.classList.add('active'); + + // Update filter and refresh + currentFilter = this.dataset.filter; + refreshOrders(); + }); + }); +} + +function refreshOrders() { + const syncButton = document.getElementById('syncButton'); + const syncText = syncButton ? syncButton.querySelector('.sync-text') : null; + + if (syncButton) { + syncButton.classList.add('loading'); + syncButton.disabled = true; + if (syncText) { + syncText.textContent = 'Syncing...'; + } + } + + const statusParam = currentFilter === 'all' ? '' : currentFilter; + const url = '/api/orders' + (statusParam ? '?status=' + statusParam : ''); + + fetch(url) + .then(response => response.json()) + .then(data => { + if (!data.error) { + updateOrderCards(data.orders); + updateStats(data.stats); + + // Connection successful - reset local connection failure counter + if (connectionStatus.local.consecutiveFailures > 0) { + connectionStatus.local.consecutiveFailures = 0; + connectionStatus.local.status = 'online'; + updateConnectionUI('local', 'online', 'Connected'); + } + } + }) + .catch(error => { + console.error('Failed to refresh orders:', error); + + // Mark local connection as potentially offline + connectionStatus.local.consecutiveFailures++; + if (connectionStatus.local.consecutiveFailures >= 2) { + connectionStatus.local.status = 'offline'; + updateConnectionUI('local', 'offline', 'Disconnected'); + } + }) + .finally(() => { + if (syncButton) { + syncButton.classList.remove('loading'); + syncButton.disabled = false; + if (syncText) { + syncText.textContent = 'Sync Now'; + } + } + }); +} + +function updateStats(stats) { + if (!stats) return; + + const totalEl = document.getElementById('stat-total'); + const newEl = document.getElementById('stat-new'); + const preparingEl = document.getElementById('stat-preparing'); + const readyEl = document.getElementById('stat-ready'); + + if (totalEl) totalEl.textContent = stats.total || 0; + if (newEl) newEl.textContent = stats.new || 0; + if (preparingEl) preparingEl.textContent = stats.preparing || 0; + if (readyEl) readyEl.textContent = stats.ready || 0; +} + +function updateOrderCards(orders) { + const container = document.getElementById('ordersContainer'); + + if (!orders || orders.length === 0) { + container.innerHTML = '
No orders to display
'; + return; + } + + // Detect new orders and canceled orders for sound notifications + if (!isFirstLoad) { + const currentOrderIds = new Set(); + const currentCanceledOrderIds = new Set(); + + orders.forEach(order => { + const orderId = order.id; + const status = order.localStatus || order.status || 'new'; + + currentOrderIds.add(orderId); + + if (status === 'canceled') { + currentCanceledOrderIds.add(orderId); + } + + // Check for new orders (not in previous set) + if (!previousOrderIds.has(orderId) && status === 'new') { + console.log('New order detected:', orderId); + audioNotification.playNewOrderSound(); + } + + // Check for newly canceled orders + if (!previousCanceledOrderIds.has(orderId) && status === 'canceled') { + console.log('Order canceled:', orderId); + audioNotification.playCanceledOrderSound(); + } + }); + + // Update tracking sets + previousOrderIds = currentOrderIds; + previousCanceledOrderIds = currentCanceledOrderIds; + } else { + // On first load, just populate the tracking sets without playing sounds + orders.forEach(order => { + previousOrderIds.add(order.id); + const status = order.localStatus || order.status || 'new'; + if (status === 'canceled') { + previousCanceledOrderIds.add(order.id); + } + }); + isFirstLoad = false; + } + + container.innerHTML = orders.map(order => createOrderCard(order)).join(''); +} + +function createOrderCard(order) { + const status = order.localStatus || order.status || 'new'; + const statusBadge = getStatusBadge(status); + const orderType = order.order.type || 'pickup'; + const typeIcon = orderType === 'delivery' ? '🚚' : '🛍️'; + + // Determine which actions to show based on status + let actions = ''; + if (status === 'new') { + actions = ` + + `; + } else if (status === 'preparing') { + actions = ` + + `; + } else if (status === 'ready') { + actions = ` + + `; + } + + // Add cancel and reprint buttons for non-completed orders + if (status !== 'canceled' && status !== 'completed') { + actions += ` + + `; + } + + actions += ` + + + `; + + // Build detailed items list with addons and excludes + let itemsDetailedHtml = ''; + if (order.order.items && order.order.items.length > 0) { + // Sort items alphabetically by name for better kitchen organization + const sortedItems = [...order.order.items].sort((a, b) => { + const nameA = (a.itemName || a.name || 'Item').toLowerCase(); + const nameB = (b.itemName || b.name || 'Item').toLowerCase(); + return nameA.localeCompare(nameB); + }); + + itemsDetailedHtml = sortedItems.map(item => { + let modifiersHtml = ''; + + // Add addons + if (item.addons && item.addons.length > 0) { + const addonsList = item.addons.map(a => a.name || a).join(', '); + modifiersHtml += `
+ Add: ${addonsList}
`; + } + + // Add excludes + if (item.exclude && item.exclude.length > 0) { + const excludeList = item.exclude.map(e => e.name || e).join(', '); + modifiersHtml += `
− NO: ${excludeList}
`; + } + + return ` +
+
+ ${item.qty}x + ${item.itemName || item.name || 'Item'} +
+ ${modifiersHtml} +
+ `; + }).join(''); + } + + // Food allergy warning (prominent) + let allergyWarning = ''; + if (order.order.foodAllergy) { + const allergyNotes = order.order.foodAllergyNotes || 'Customer has food allergies'; + allergyWarning = ` +
+
⚠️
+
+
FOOD ALLERGY ALERT
+
${allergyNotes}
+
+
+ `; + } + + // Special instructions + let specialInstructionsHtml = ''; + if (order.order.specialInstructions) { + specialInstructionsHtml = ` +
+
📝 Special Instructions:
+
${order.order.specialInstructions}
+
+ `; + } + + return ` +
+
+
#${order.id}
+
${statusBadge}
+
+ + ${allergyWarning} + +
+
+ Time: + ${formatTime(order.createdAt)} +
+
+ Customer: + ${order.customer.name || 'N/A'} +
+
+ Type: + ${typeIcon}${orderType} +
+ ${order.order.deliveryAddress ? `
Address:${order.order.deliveryAddress}
` : ''} +
+ +
+
Order Items:
+ ${itemsDetailedHtml} +
+ + ${specialInstructionsHtml} + +
+ Total: + ${formatCurrency(order.totalAmount)} +
+ +
+ ${actions} +
+
+ `; +} + +function getStatusBadge(status) { + const badges = { + 'new': 'New', + 'preparing': 'Preparing', + 'ready': 'Ready', + 'completed': 'Completed', + 'canceled': 'Canceled' + }; + return badges[status] || status; +} + +function changeStatus(orderId, newStatus) { + // Fetch the order data first to show in the confirmation modal + fetch(`/api/orders/${orderId}`) + .then(response => response.json()) + .then(data => { + if (!data.error && data.order) { + openStatusChangeModal(orderId, newStatus, data.order); + } else { + showToast('Failed to fetch order details', 'error'); + } + }) + .catch(error => { + console.error('Error fetching order:', error); + showToast('Failed to fetch order details', 'error'); + }); +} + +function openStatusChangeModal(orderId, newStatus, orderData) { + const currentStatus = orderData.localStatus || orderData.status || 'new'; + + // Store pending status change + pendingStatusChange = { + orderId: orderId, + newStatus: newStatus, + currentStatus: currentStatus, + orderData: orderData + }; + + // Get status information + const statusInfo = getStatusInfo(newStatus); + + // Update modal icon + const iconEl = document.getElementById('statusModalIcon'); + iconEl.textContent = statusInfo.icon; + iconEl.className = 'status-modal-icon ' + newStatus; + + // Update modal title + document.getElementById('statusModalTitle').textContent = statusInfo.title; + + // Update order details + document.getElementById('statusModalOrderId').textContent = orderId; + document.getElementById('statusModalCustomer').textContent = orderData.customer.name || 'N/A'; + + // Update status badges + const fromBadge = document.getElementById('statusModalFrom'); + const toBadge = document.getElementById('statusModalTo'); + + fromBadge.textContent = getStatusBadge(currentStatus); + fromBadge.className = 'status-badge badge-' + currentStatus; + + toBadge.textContent = getStatusBadge(newStatus); + toBadge.className = 'status-badge badge-' + newStatus; + + // Update message + document.getElementById('statusModalMessage').textContent = statusInfo.message; + + // Update confirm button + const confirmBtn = document.getElementById('statusConfirmBtn'); + confirmBtn.textContent = statusInfo.buttonText; + + // Show modal + document.getElementById('statusChangeModal').classList.add('visible'); +} + +function getStatusInfo(status) { + const statusMap = { + 'preparing': { + icon: '👨‍🍳', + title: 'Start Preparing Order?', + message: 'This will mark the order as being prepared in the kitchen.', + buttonText: 'Start Preparing' + }, + 'ready': { + icon: '✅', + title: 'Mark Order as Ready?', + message: 'This will notify that the order is ready for pickup or delivery.', + buttonText: 'Mark Ready' + }, + 'completed': { + icon: '🎉', + title: 'Complete Order?', + message: 'This will mark the order as completed and it will be archived.', + buttonText: 'Complete Order' + } + }; + + return statusMap[status] || { + icon: '📋', + title: 'Change Order Status?', + message: 'This will update the order status.', + buttonText: 'Confirm' + }; +} + +function closeStatusChangeModal() { + document.getElementById('statusChangeModal').classList.remove('visible'); + pendingStatusChange = { + orderId: null, + newStatus: null, + currentStatus: null, + orderData: null + }; +} + +function confirmStatusChange() { + if (!pendingStatusChange.orderId || !pendingStatusChange.newStatus) { + return; + } + + const orderId = pendingStatusChange.orderId; + const newStatus = pendingStatusChange.newStatus; + + // Close modal immediately for better UX + closeStatusChangeModal(); + + // Make the API call + fetch(`/api/orders/${orderId}/status`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ status: newStatus }) + }) + .then(response => response.json()) + .then(data => { + if (!data.error) { + showToast(`Order #${orderId} updated to ${newStatus}`, 'success'); + refreshOrders(); + } else { + showToast(data.message || 'Failed to update order', 'error'); + } + }) + .catch(error => { + console.error('Error updating order:', error); + showToast('Failed to update order', 'error'); + }); +} + +function openCancelModal(orderId) { + currentOrderIdForCancel = orderId; + document.getElementById('cancelReason').value = ''; + document.getElementById('cancelModal').classList.add('visible'); +} + +function closeCancelModal() { + currentOrderIdForCancel = null; + document.getElementById('cancelModal').classList.remove('visible'); +} + +function confirmCancel() { + if (!currentOrderIdForCancel) return; + + const reason = document.getElementById('cancelReason').value; + + fetch(`/api/orders/${currentOrderIdForCancel}/cancel`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ reason: reason || 'Canceled by kitchen' }) + }) + .then(response => response.json()) + .then(data => { + if (!data.error) { + showToast(`Order #${currentOrderIdForCancel} canceled`, 'success'); + closeCancelModal(); + refreshOrders(); + } else { + showToast(data.message || 'Failed to cancel order', 'error'); + } + }) + .catch(error => { + console.error('Error canceling order:', error); + showToast('Failed to cancel order', 'error'); + }); +} + +let reprintInProgressByOrder = {}; +function reprintOrder(orderId) { + if (reprintInProgressByOrder[orderId]) { + return; // guard against rapid double clicks + } + reprintInProgressByOrder[orderId] = true; + fetch(`/api/orders/${orderId}/reprint`, { + method: 'POST' + }) + .then(response => response.json()) + .then(data => { + if (!data.error) { + showToast((data && data.message) ? data.message : 'Receipt sent to printer', 'success'); + } else { + showToast(data.message || 'Failed to print receipt', 'error'); + } + }) + .catch(error => { + console.error('Error reprinting order:', error); + showToast('Failed to print receipt', 'error'); + }) + .finally(() => { + reprintInProgressByOrder[orderId] = false; + }); +} + +function showOrderDetails(orderId) { + fetch(`/api/orders/${orderId}`) + .then(response => response.json()) + .then(data => { + if (!data.error && data.order) { + displayOrderDetails(data.order); + } + }) + .catch(error => { + console.error('Error fetching order details:', error); + }); +} + +function displayOrderDetails(order) { + const modal = document.getElementById('detailsModal'); + const content = document.getElementById('orderDetailsContent'); + + let itemsHtml = ''; + if (order.order.items && order.order.items.length > 0) { + itemsHtml = order.order.items.map(item => { + let modifiersHtml = ''; + + // Add addons with prices if available + if (item.addons && item.addons.length > 0) { + item.addons.forEach(addon => { + const addonName = addon.name || addon; + const addonPrice = typeof addon === 'object' && addon.price != null ? addon.price : null; + + if (addonPrice != null && addonPrice > 0) { + modifiersHtml += `
+ ${addonName} (+${formatCurrency(addonPrice)})`; + } else if (addonPrice === 0) { + modifiersHtml += `
+ ${addonName} (Free)`; + } else { + modifiersHtml += `
+ ${addonName}`; + } + }); + } + + // Add excludes (no price) + if (item.exclude && item.exclude.length > 0) { + item.exclude.forEach(exc => { + const excName = exc.name || exc; + modifiersHtml += `
− NO ${excName}`; + }); + } + + return ` + + ${item.qty}x + + ${item.itemName || item.name} + ${modifiersHtml} + + ${formatCurrency(item.price * item.qty)} + + `; + }).join(''); + } + + content.innerHTML = ` +
+
+

Order Information

+

Order ID: ${order.id}

+

Status: ${getStatusBadge(order.localStatus || order.status)}

+

Time: ${formatDateTime(order.createdAt)}

+

Type: ${order.order.type || 'N/A'}

+
+ +
+

Customer Information

+

Name: ${order.customer.name || 'N/A'}

+

Phone: ${order.customer.phoneNumber || 'N/A'}

+ ${order.customer.email ? '

Email: ' + order.customer.email + '

' : ''} + ${order.order.deliveryAddress ? '

Address: ' + order.order.deliveryAddress + '

' : ''} +
+ +
+

Order Items

+ + + + + + + + + + ${itemsHtml} + + + + + + + ${order.order.taxAmount ? ` + + + + ` : ''} + ${order.order.deliveryFee ? ` + + + + ` : ''} + + + + + +
QtyItemPrice
Subtotal:${formatCurrency(order.order.amount)}
Tax (${order.order.taxRate || 0}%):${formatCurrency(order.order.taxAmount)}
Delivery Fee:${formatCurrency(order.order.deliveryFee)}
TOTAL:${formatCurrency(order.totalAmount)}
+
+ + ${order.order.specialInstructions ? ` +
+

Special Instructions

+

${order.order.specialInstructions}

+
` : ''} + + ${order.order.deliveryInstructions ? ` +
+

Delivery Instructions

+

${order.order.deliveryInstructions}

+
` : ''} + + ${order.order.foodAllergy ? ` +
+

⚠ Food Allergy Warning

+

${order.order.foodAllergyNotes || 'Customer has food allergies'}

+
` : ''} +
+ `; + + modal.classList.add('visible'); +} + +function closeDetailsModal() { + document.getElementById('detailsModal').classList.remove('visible'); +} + +// Close modals when clicking outside +window.addEventListener('click', function(event) { + const statusChangeModal = document.getElementById('statusChangeModal'); + const cancelModal = document.getElementById('cancelModal'); + const detailsModal = document.getElementById('detailsModal'); + + if (event.target === statusChangeModal) { + closeStatusChangeModal(); + } + + if (event.target === cancelModal) { + closeCancelModal(); + } + + if (event.target === detailsModal) { + closeDetailsModal(); + } +}); + +// Close modals with Escape key +document.addEventListener('keydown', function(event) { + if (event.key === 'Escape') { + closeStatusChangeModal(); + closeCancelModal(); + closeDetailsModal(); + } +}); + +// Connection status monitoring +function checkConnectionStatus() { + // Check local connection (dashboard server) + checkLocalConnection(); + + // Check external API connection + checkAPIConnection(); +} + +function checkLocalConnection() { + const startTime = Date.now(); + + fetch('/api/health/local') + .then(response => { + if (response.ok) { + return response.json(); + } else { + throw new Error('Server returned error status'); + } + }) + .then(data => { + connectionStatus.local.status = 'online'; + connectionStatus.local.lastCheck = new Date(); + connectionStatus.local.consecutiveFailures = 0; + updateConnectionUI('local', 'online', 'Connected'); + }) + .catch(error => { + console.error('Local connection check failed:', error); + connectionStatus.local.consecutiveFailures++; + + if (connectionStatus.local.consecutiveFailures >= 2) { + connectionStatus.local.status = 'offline'; + updateConnectionUI('local', 'offline', 'Disconnected'); + + // Show warning toast on first detection of offline + if (connectionStatus.local.consecutiveFailures === 2) { + showToast('Dashboard server connection lost', 'error'); + } + } + }); +} + +function checkAPIConnection() { + const startTime = Date.now(); + + fetch('/api/health/external') + .then(response => { + if (response.ok) { + return response.json(); + } else { + throw new Error('Server returned error status'); + } + }) + .then(data => { + connectionStatus.api.lastCheck = new Date(); + + if (data.status === 'online') { + // Was offline, now online - notify user + if (connectionStatus.api.status === 'offline' && connectionStatus.api.consecutiveFailures >= 2) { + showToast('API server connection restored', 'success'); + } + + connectionStatus.api.status = 'online'; + connectionStatus.api.consecutiveFailures = 0; + connectionStatus.api.responseTime = data.responseTime; + + let label = 'Connected'; + if (data.responseTime) { + label = `${data.responseTime}ms`; + } + updateConnectionUI('api', 'online', label); + } else if (data.status === 'unconfigured') { + connectionStatus.api.status = 'unconfigured'; + connectionStatus.api.consecutiveFailures = 0; + updateConnectionUI('api', 'unconfigured', 'Not Configured'); + } else { + connectionStatus.api.consecutiveFailures++; + + if (connectionStatus.api.consecutiveFailures >= 2) { + connectionStatus.api.status = 'offline'; + updateConnectionUI('api', 'offline', data.message || 'Disconnected'); + + // Show warning toast on first detection of offline + if (connectionStatus.api.consecutiveFailures === 2) { + showToast('API server connection lost: ' + (data.message || 'Disconnected'), 'error'); + } + } + } + }) + .catch(error => { + console.error('API connection check failed:', error); + connectionStatus.api.consecutiveFailures++; + + if (connectionStatus.api.consecutiveFailures >= 2) { + connectionStatus.api.status = 'offline'; + updateConnectionUI('api', 'offline', 'Check Failed'); + } + }); +} + +function updateConnectionUI(type, status, label) { + let statusItem, statusIndicator, statusLabel; + + if (type === 'local') { + statusItem = document.getElementById('localConnectionStatus'); + statusLabel = document.getElementById('localStatusLabel'); + } else if (type === 'api') { + statusItem = document.getElementById('apiConnectionStatus'); + statusLabel = document.getElementById('apiStatusLabel'); + } + + if (!statusItem || !statusLabel) return; + + statusIndicator = statusItem.querySelector('.status-indicator'); + + // Remove all status classes + statusIndicator.classList.remove('status-online', 'status-offline', 'status-checking', 'status-unconfigured'); + + // Add new status class + statusIndicator.classList.add('status-' + status); + + // Update label text + statusLabel.textContent = label; + + // Update title attribute for tooltip + let tooltipText = ''; + if (type === 'local') { + tooltipText = 'Dashboard Server: '; + } else { + tooltipText = 'API Server (api.thinklink.ai): '; + } + + tooltipText += label; + if (connectionStatus[type].lastCheck) { + const lastCheckTime = connectionStatus[type].lastCheck.toLocaleTimeString(); + tooltipText += ' (Last checked: ' + lastCheckTime + ')'; + } + + statusItem.title = tooltipText; +} + +// Manual sync trigger +function manualSync() { + const syncButton = document.getElementById('syncButton'); + const syncText = syncButton ? syncButton.querySelector('.sync-text') : null; + + // Prevent multiple simultaneous sync requests + if (syncButton && syncButton.classList.contains('loading')) { + return; + } + + if (syncButton) { + syncButton.classList.add('loading'); + syncButton.disabled = true; + if (syncText) { + syncText.textContent = 'Syncing...'; + } + } + + fetch('/api/sync-now', { + method: 'POST' + }) + .then(response => response.json()) + .then(data => { + if (!data.error) { + showToast('Checking for new orders...', 'success'); + // Refresh after a short delay to allow sync to complete + setTimeout(() => { + refreshOrders(); + }, 2000); + } else { + showToast(data.message || 'Sync failed', 'error'); + // Remove loading state on error + if (syncButton) { + syncButton.classList.remove('loading'); + syncButton.disabled = false; + if (syncText) { + syncText.textContent = 'Sync Now'; + } + } + } + }) + .catch(error => { + console.error('Manual sync error:', error); + showToast('Sync failed', 'error'); + // Remove loading state on error + if (syncButton) { + syncButton.classList.remove('loading'); + syncButton.disabled = false; + if (syncText) { + syncText.textContent = 'Sync Now'; + } + } + }); +} + diff --git a/public/js/settings.js b/public/js/settings.js new file mode 100644 index 0000000..2918179 --- /dev/null +++ b/public/js/settings.js @@ -0,0 +1,709 @@ +// Settings page functionality + +// ========== Legacy functions (kept for backward compatibility) ========== +function testPrinter() { + const resultEl = document.getElementById('printerTestResult'); + const selectedHidden = document.getElementById('selectedPrintersJson'); + resultEl.textContent = 'Testing...'; + resultEl.style.color = '#666'; + + const body = JSON.stringify({ + selectedPrintersJson: selectedHidden ? selectedHidden.value : '[]' + }); + + fetch('/settings/test-printer', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body + }) + .then(response => response.json()) + .then(data => { + if (!data.error) { + resultEl.textContent = '✓ ' + data.message; + resultEl.style.color = '#28a745'; + } else { + resultEl.textContent = '✗ ' + (data.message || 'Test failed'); + resultEl.style.color = '#dc3545'; + } + }) + .catch(error => { + console.error('Test print error:', error); + resultEl.textContent = '✗ Network error'; + resultEl.style.color = '#dc3545'; + }); +} + +async function uploadLogo() { + const fileInput = document.getElementById('logoUpload'); + const file = fileInput.files[0]; + + if (!file) { + alert('Please select a file first'); + return; + } + + const validTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']; + if (!validTypes.includes(file.type)) { + alert('Please select a valid image file (PNG, JPG, or GIF)'); + return; + } + + if (file.size > 5 * 1024 * 1024) { + alert('File size must be less than 5MB'); + return; + } + + const formData = new FormData(); + formData.append('file', file); + + try { + const response = await fetch('/settings/upload-logo', { + method: 'POST', + body: formData + }); + + const data = await response.json(); + + if (!data.error) { + alert('Logo uploaded successfully!'); + location.reload(); + } else { + alert('Upload failed: ' + (data.message || 'Unknown error')); + } + } catch (error) { + console.error('Logo upload error:', error); + alert('Upload failed: Network error'); + } +} + +// ========== New Printer Management Functions ========== + +let currentPrinterId = null; + +// Load and display printers +async function loadPrinters() { + const container = document.getElementById('printer-cards-container'); + if (!container) return; + + try { + const response = await fetch('/api/printers/list'); + const data = await response.json(); + + if (data.error || !data.printers || data.printers.length === 0) { + container.innerHTML = ` +
+

No printers configured yet.

+

Click "Add Printer" to configure your first printer.

+
+ `; + return; + } + + // Render printer cards + const cardsHTML = data.printers.map(printer => createPrinterCard(printer)).join(''); + container.innerHTML = cardsHTML; + } catch (error) { + console.error('Failed to load printers:', error); + container.innerHTML = `
Failed to load printers: ${error.message}
`; + } +} + +// Create HTML for a printer card +function createPrinterCard(printer) { + const defaultBadge = printer.is_default ? 'DEFAULT' : ''; + const enabledBadge = printer.is_enabled + ? 'ENABLED' + : 'DISABLED'; + + const typeLabel = { + 'network': 'Network', + 'com': 'Serial/COM', + 'usb': 'USB', + 'system': 'System Printer' + }[printer.type] || printer.type; + + return ` +
+
+
+

${escapeHtml(printer.name)}

+
+ ${defaultBadge} + ${enabledBadge} +
+
+
+
+
+ Connection: + ${typeLabel}: ${escapeHtml(printer.interface)} +
+
+ Paper: + ${printer.paper_format} (${printer.paper_width} chars) | ${printer.printer_type} +
+
+
+ + + ${!printer.is_default ? `` : ''} + + ${!printer.is_default ? `` : ''} +
+
+ `; +} + +// Escape HTML to prevent XSS +function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; +} + +// Open add printer modal +function openAddPrinterModal() { + currentPrinterId = null; + document.getElementById('printerModalTitle').textContent = 'Add Printer'; + document.getElementById('printerConfigForm').reset(); + document.getElementById('printer_id').value = ''; + + // Set defaults + document.getElementById('paper_width').value = 48; + document.getElementById('paper_format').value = '80mm'; + document.getElementById('printer_type_model').value = 'epson'; + document.getElementById('font_size').value = 'normal'; + document.getElementById('line_style').value = 'single'; + document.getElementById('qr_code_size').value = 3; + document.getElementById('qr_code_correction').value = 'M'; + document.getElementById('qr_code_content_template').value = 'ORDER-{id}'; + document.getElementById('header_text').value = ''; + document.getElementById('footer_text').value = ''; + document.getElementById('business_contact_size').value = 'normal'; + + // Pre-fill Business Information from Receipt Template tab + try { + const tplBusinessName = document.getElementById('businessName'); + const tplBusinessAddress = document.getElementById('businessAddress'); + const tplBusinessPhone = document.getElementById('businessPhone'); + const tplBusinessWebsite = document.getElementById('businessWebsite'); + const tplBusinessEmail = document.getElementById('businessEmail'); + const tplBusinessContactSize = document.getElementById('businessContactSize'); + + if (tplBusinessName) document.getElementById('business_name').value = tplBusinessName.value || ''; + if (tplBusinessAddress) document.getElementById('business_address').value = tplBusinessAddress.value || ''; + if (tplBusinessPhone) document.getElementById('business_phone').value = tplBusinessPhone.value || ''; + if (tplBusinessWebsite) document.getElementById('business_website').value = tplBusinessWebsite.value || ''; + if (tplBusinessEmail) document.getElementById('business_email').value = tplBusinessEmail.value || ''; + if (tplBusinessContactSize) document.getElementById('business_contact_size').value = tplBusinessContactSize.value || 'normal'; + } catch (_) {} + + // Set checkboxes + document.getElementById('show_customer_info').checked = true; + document.getElementById('show_order_items').checked = true; + document.getElementById('show_prices').checked = true; + document.getElementById('show_timestamps').checked = true; + document.getElementById('qr_code_enabled').checked = true; + document.getElementById('is_enabled').checked = true; + document.getElementById('is_default').checked = false; + + showModal('printerConfigModal'); + switchPrinterModalTab('connection'); +} + +// Edit printer +async function editPrinter(id) { + try { + const response = await fetch(`/api/printers/${id}`); + const data = await response.json(); + + if (data.error) { + alert('Failed to load printer: ' + data.message); + return; + } + + const printer = data.printer; + currentPrinterId = id; + + document.getElementById('printerModalTitle').textContent = 'Edit Printer'; + document.getElementById('printer_id').value = id; + + // Fill form with printer data + document.getElementById('printer_name').value = printer.name || ''; + document.getElementById('printer_type_select').value = printer.type || 'network'; + document.getElementById('printer_interface').value = printer.interface || ''; + document.getElementById('paper_format').value = printer.paper_format || '80mm'; + document.getElementById('paper_width').value = printer.paper_width || 48; + document.getElementById('printer_type_model').value = printer.printer_type || 'epson'; + document.getElementById('font_size').value = printer.font_size || 'normal'; + document.getElementById('line_style').value = printer.line_style || 'single'; + + document.getElementById('header_text').value = printer.header_text || ''; + document.getElementById('footer_text').value = printer.footer_text || ''; + + document.getElementById('business_name').value = printer.business_name || ''; + document.getElementById('business_address').value = printer.business_address || ''; + document.getElementById('business_phone').value = printer.business_phone || ''; + document.getElementById('business_website').value = printer.business_website || ''; + document.getElementById('business_email').value = printer.business_email || ''; + document.getElementById('business_contact_size').value = printer.business_contact_size || 'normal'; + + document.getElementById('logo_path').value = printer.logo_path || ''; + document.getElementById('logo_max_width_dots').value = printer.logo_max_width_dots || ''; + + if (printer.logo_path) { + document.getElementById('logo_preview').innerHTML = `Current logo: ${printer.logo_path}`; + } else { + document.getElementById('logo_preview').innerHTML = ''; + } + + document.getElementById('qr_code_size').value = printer.qr_code_size || 3; + document.getElementById('qr_code_correction').value = printer.qr_code_correction || 'M'; + document.getElementById('qr_code_content_template').value = printer.qr_code_content_template || 'ORDER-{id}'; + + document.getElementById('show_customer_info').checked = printer.show_customer_info !== false; + document.getElementById('show_order_items').checked = printer.show_order_items !== false; + document.getElementById('show_prices').checked = printer.show_prices !== false; + document.getElementById('show_timestamps').checked = printer.show_timestamps !== false; + document.getElementById('qr_code_enabled').checked = printer.qr_code_enabled !== false; + document.getElementById('is_default').checked = printer.is_default || false; + document.getElementById('is_enabled').checked = printer.is_enabled !== false; + + showModal('printerConfigModal'); + switchPrinterModalTab('connection'); + } catch (error) { + console.error('Failed to load printer:', error); + alert('Failed to load printer: ' + error.message); + } +} + +// Save printer configuration +async function savePrinterConfig() { + const form = document.getElementById('printerConfigForm'); + + if (!form.checkValidity()) { + form.reportValidity(); + return; + } + + const formData = new FormData(form); + const config = {}; + + for (const [key, value] of formData.entries()) { + if (key === 'id' && !value) continue; // Skip empty id + + // Handle checkboxes + if (['show_customer_info', 'show_order_items', 'show_prices', 'show_timestamps', 'qr_code_enabled', 'is_default', 'is_enabled'].includes(key)) { + config[key] = document.getElementById(key).checked; + } else if (key === 'paper_width' || key === 'qr_code_size' || key === 'logo_max_width_dots') { + const val = parseInt(value, 10); + if (!isNaN(val) && val > 0) config[key] = val; + } else { + config[key] = value; + } + } + + // Ensure checkbox fields are always present in payload (unchecked boxes are omitted from FormData by default) + ['show_customer_info', 'show_order_items', 'show_prices', 'show_timestamps', 'qr_code_enabled', 'is_default', 'is_enabled'].forEach((key) => { + const el = document.getElementById(key); + if (el) { + config[key] = !!el.checked; + } + }); + + try { + const printerId = document.getElementById('printer_id').value; + const url = printerId ? `/api/printers/${printerId}` : '/api/printers/create'; + const method = printerId ? 'PUT' : 'POST'; + + const response = await fetch(url, { + method: method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(config) + }); + + const data = await response.json(); + + if (data.error) { + alert('Failed to save printer: ' + data.message); + return; + } + + alert(data.message || 'Printer saved successfully'); + closePrinterModal(); + loadPrinters(); + } catch (error) { + console.error('Failed to save printer:', error); + alert('Failed to save printer: ' + error.message); + } +} + +// Delete printer +async function deletePrinter(id) { + if (!confirm('Are you sure you want to delete this printer?')) { + return; + } + + try { + const response = await fetch(`/api/printers/${id}`, { method: 'DELETE' }); + const data = await response.json(); + + if (data.error) { + alert('Failed to delete printer: ' + data.message); + return; + } + + alert('Printer deleted successfully'); + loadPrinters(); + } catch (error) { + console.error('Failed to delete printer:', error); + alert('Failed to delete printer: ' + error.message); + } +} + +// Test printer +async function testPrinterById(id) { + try { + const response = await fetch(`/api/printers/${id}/test`, { method: 'POST' }); + const data = await response.json(); + + if (data.error) { + alert('Test failed: ' + data.message); + } else { + alert(data.message || 'Test print sent successfully'); + } + } catch (error) { + console.error('Test print error:', error); + alert('Test failed: ' + error.message); + } +} + +// Set default printer +async function setDefaultPrinter(id) { + try { + const response = await fetch(`/api/printers/${id}/set-default`, { method: 'POST' }); + const data = await response.json(); + + if (data.error) { + alert('Failed to set default: ' + data.message); + } else { + loadPrinters(); + } + } catch (error) { + console.error('Failed to set default:', error); + alert('Failed to set default: ' + error.message); + } +} + +// Toggle printer enabled +async function togglePrinterEnabled(id) { + try { + const response = await fetch(`/api/printers/${id}/toggle-enabled`, { method: 'POST' }); + const data = await response.json(); + + if (data.error) { + alert('Failed to toggle printer: ' + data.message); + } else { + loadPrinters(); + } + } catch (error) { + console.error('Failed to toggle printer:', error); + alert('Failed to toggle printer: ' + error.message); + } +} + +// Upload logo for specific printer +async function uploadLogoForPrinter() { + const fileInput = document.getElementById('logo_upload_modal'); + const file = fileInput.files[0]; + + if (!file) { + alert('Please select a file first'); + return; + } + + const validTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']; + if (!validTypes.includes(file.type)) { + alert('Please select a valid image file (PNG, JPG, or GIF)'); + return; + } + + if (file.size > 5 * 1024 * 1024) { + alert('File size must be less than 5MB'); + return; + } + + const formData = new FormData(); + formData.append('file', file); + + // If editing existing printer, include printer_id + const printerId = document.getElementById('printer_id').value; + if (printerId) { + formData.append('printer_id', printerId); + } + + try { + const response = await fetch('/settings/upload-logo', { + method: 'POST', + body: formData + }); + + const data = await response.json(); + + if (!data.error) { + alert('Logo uploaded successfully'); + document.getElementById('logo_path').value = data.filepath; + document.getElementById('logo_preview').innerHTML = `Logo uploaded: ${data.filepath}`; + } else { + alert('Upload failed: ' + (data.message || 'Unknown error')); + } + } catch (error) { + console.error('Logo upload error:', error); + alert('Upload failed: Network error'); + } +} + +// Detect printers for modal +async function detectPrintersForModal() { + const listEl = document.getElementById('detected-printers-list'); + listEl.innerHTML = '

Detecting printers...

'; + + try { + const response = await fetch('/api/printers/detect'); + const data = await response.json(); + + if (data.error || !data.printers || data.printers.length === 0) { + listEl.innerHTML = '

No printers detected.

'; + return; + } + + const items = data.printers.map(p => { + const typeLabel = { + 'system': 'System', + 'com': 'COM' + }[p.type] || p.type; + + return ` +
+ ${escapeHtml(p.name)} (${typeLabel}) + +
+ `; + }).join(''); + + listEl.innerHTML = items; + } catch (error) { + console.error('Failed to detect printers:', error); + listEl.innerHTML = '

Failed to detect printers.

'; + } +} + +// Select a detected printer +function selectDetectedPrinter(type, interface) { + document.getElementById('printer_type_select').value = type; + document.getElementById('printer_interface').value = interface; + updateInterfaceHint(); +} + +// Update interface hint based on connection type +function updateInterfaceHint() { + const type = document.getElementById('printer_type_select').value; + const hintEl = document.getElementById('interface_hint'); + + const hints = { + 'network': 'Enter IP:Port for network printers (e.g., 192.168.1.100:9100)', + 'com': 'Enter COM port (e.g., COM1, COM3)', + 'usb': 'Enter USB device path (e.g., /dev/usb/lp0)', + 'system': 'Enter the exact printer name from Windows' + }; + + hintEl.textContent = hints[type] || 'Enter connection address'; +} + +// Auto-update paper width when format changes +function updatePaperWidthFromFormat() { + const format = document.getElementById('paper_format').value; + const widthInput = document.getElementById('paper_width'); + + const widthMap = { + '58mm': 32, + '80mm': 48, + 'letter': 80 + }; + + if (widthMap[format]) { + widthInput.value = widthMap[format]; + } +} + +// Modal tab switching +function switchPrinterModalTab(tabName) { + // Update tab buttons + document.querySelectorAll('.printer-modal-tab-btn').forEach(btn => { + btn.classList.remove('active'); + if (btn.getAttribute('data-tab') === tabName) { + btn.classList.add('active'); + } + }); + + // Update tab content + document.querySelectorAll('.printer-modal-tab-content').forEach(content => { + content.classList.remove('active'); + }); + document.getElementById(tabName + '-tab-content').classList.add('active'); +} + +// Close printer modal +function closePrinterModal() { + hideModal('printerConfigModal'); + currentPrinterId = null; +} + +// Show modal +function showModal(modalId) { + document.getElementById(modalId).classList.add('visible'); +} + +// Hide modal +function hideModal(modalId) { + document.getElementById(modalId).classList.remove('visible'); +} + +// ========== Sound Notification Functions ========== + +// Upload sound file +async function uploadSound(soundType) { + const fileInputId = soundType === 'newOrder' ? 'newOrderSoundUpload' : 'canceledOrderSoundUpload'; + const fileInput = document.getElementById(fileInputId); + const file = fileInput.files[0]; + + if (!file) { + alert('Please select a file first'); + return; + } + + const validTypes = ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/ogg']; + if (!validTypes.includes(file.type)) { + alert('Please select a valid audio file (MP3, WAV, or OGG)'); + return; + } + + if (file.size > 10 * 1024 * 1024) { + alert('File size must be less than 10MB'); + return; + } + + const formData = new FormData(); + formData.append('file', file); + formData.append('soundType', soundType); + + try { + const response = await fetch('/settings/upload-sound', { + method: 'POST', + body: formData + }); + + const data = await response.json(); + + if (!data.error) { + alert('Sound uploaded successfully! Please save settings to apply changes.'); + location.reload(); + } else { + alert('Upload failed: ' + (data.message || 'Unknown error')); + } + } catch (error) { + console.error('Sound upload error:', error); + alert('Upload failed: Network error'); + } +} + +// Test sound playback +async function testSound(soundType) { + try { + // Get current settings + const response = await fetch('/api/notification-settings'); + const data = await response.json(); + + if (data.error) { + alert('Failed to load sound settings'); + return; + } + + const soundPath = soundType === 'newOrder' + ? (data.newOrderSoundPath || '/public/sounds/new-order-notification.mp3') + : (data.canceledOrderSoundPath || '/public/sounds/canceled-order-notification.mp3'); + + const volumeInput = document.getElementById('soundVolume'); + const volume = volumeInput ? parseInt(volumeInput.value, 10) / 100 : 0.8; + + const audio = new Audio(soundPath); + audio.volume = volume; + + audio.play().catch(error => { + console.error('Failed to play sound:', error); + alert('Failed to play sound. Make sure the file exists and is a valid audio file.'); + }); + } catch (error) { + console.error('Test sound error:', error); + alert('Failed to test sound: ' + error.message); + } +} + +// Update volume display +function updateVolumeDisplay() { + const volumeInput = document.getElementById('soundVolume'); + const volumeValue = document.getElementById('volumeValue'); + + if (volumeInput && volumeValue) { + volumeValue.textContent = volumeInput.value; + } +} + +// ========== Event Listeners ========== +document.addEventListener('DOMContentLoaded', function() { + // Load printers on settings page + if (document.getElementById('printer-cards-container')) { + loadPrinters(); + } + + // Printer modal tab switching + document.querySelectorAll('.printer-modal-tab-btn').forEach(btn => { + btn.addEventListener('click', function() { + switchPrinterModalTab(this.getAttribute('data-tab')); + }); + }); + + // Connection type change handler + const typeSelect = document.getElementById('printer_type_select'); + if (typeSelect) { + typeSelect.addEventListener('change', updateInterfaceHint); + } + + // Paper format change handler + const formatSelect = document.getElementById('paper_format'); + if (formatSelect) { + formatSelect.addEventListener('change', updatePaperWidthFromFormat); + } + + // Volume slider handler + const volumeInput = document.getElementById('soundVolume'); + if (volumeInput) { + volumeInput.addEventListener('input', updateVolumeDisplay); + } + + // Update interface placeholder based on interface type (legacy support) + const interfaceSelect = document.getElementById('printerInterface'); + const pathInput = document.getElementById('printerPath'); + + if (interfaceSelect && pathInput) { + interfaceSelect.addEventListener('change', function() { + const placeholders = { + 'usb': '/dev/usb/lp0 (Linux) or COM1 (Windows)', + 'network': '192.168.1.100:9100', + 'serial': 'COM1 (Windows) or /dev/ttyS0 (Linux)' + }; + + pathInput.placeholder = placeholders[this.value] || 'Enter printer path'; + }); + } +}); diff --git a/public/sounds/canceled-order-notification.mp3 b/public/sounds/canceled-order-notification.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..fb10e018938e7ae3f88c872d89121f869fb37b67 GIT binary patch literal 33024 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%0ABS;uJ@105=rVF&p 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 z z&8kQ|iCMwU!l;mQ3qmn;k_J!WvuOiMK@8-(EXb*PbcE}eg$& z*qx~;!ds6ya^EY|mED`GR-Y@h(fqlHr7|U<1|V9ML@8Qu$8?aNPX3IqTO`*+%{yAobzp~%?qnTjP zTd<%=DwMRdoT~7$bOwpxGb7!FqdAo=9ob&)QA+5jzAZT~tie8;Qu=#T@CgYtTa8wy z84T2*mzjf>NY5N_+VNu(E`qtrCAX(w%NmYVdnilUBHA&__M?3 zQjh=q{-A8@t|l*=ER3La@L8NgWKU22{;p)LPoQ_voRl1ul75O_nm@lqI-ot@*R_jx zG&0!})<$ncE|S*tr+}kQ@a-Q7DSOj!k+tf1igw0)(AepCkUScP-|lR9G-ZzoFHp>M0Eebkt=)5{zcDR)+agS8#^Xzo zjyNyKu8my#nODu;1`8JBW~YMvQlJ7jCn)CX|a%^|&)<4e$}VJ{9{Jy3aE31qHI_%9+;$!Y1|G)I*1 z)b*C+-(*Gc?O<7rgxP^K`#?_m#QFthb~G|4ZV=?xsV9BU< zDXU(mdHVVlXOPIP3O7UD#pm&@MUr-+RP-ymAj3svf~=rH*TrH1mbco!Ltf&<1Y>lM z0smv@Ji&-QuoqiHi}kZ7bul9DE6w@ie&~@QMVSJ__mu>e%zohENu>nm6f!YTGzac% zU7|=oHl&3)udM%ozOH$8qrz;{GhhxVfn!QTr_nxD$zf>vS%6rE6-~i6D!RsV5Ew39 z5+lmn$>3CV7*#SSwur`mbci?U8uV6-?Dt=H)D2-aZ@a^y;4MDe!5btK*O1VAUvD3U zr5ZKNwErOHQZ9eGP}GLZ$t0wG#YCZ?H@1)tlxc)M)g_WKva++AylT_L9d?>2Jgb-9 zJDwtP#-fn3`Mq?g7)S`!^A3lQthT2T=L|#h!qVeW^O|6eeW1fvHQb@Kc=)MN!#WQO zVKM6$yp}XQsI{hvAw3NGMW-l|`|A928Jrbxl|q_)zzdTQhbrDlGgC{km|nA@k#eJI zRT0uqBOHu9_0%j|ocZcHqO($u;*<=QX{k&~rb7aqw_?%*lg)cS7jYE{x@%Cp|2Ci? z;Yj+cT(F|oFR}bhPm}FJ!S5oe3GU4G+sflX``@qwzKf#YSA54LksgRXINsB3vXl2e zFdxmBfEOy4=ng@HOXEz%tX>iH%G=rKa=Oq|^oZ&Dl(xKD28HGI4!kdRD2R*X(qWtw zyeUZ(lw5(SAjVsZNJf13PP@-ysg0}ETO!M&Hj`JiJz+lt_rs3u$$Yg%H|>ekUd}N| z2B;A#{^-#eGCG#Zw?YkN@m38o<2(u2T=)O6(dpQuPIOVr6q#W$5^uvh%=D4isMmo1 zC4BNO-hI?oriH^EM<&X{zRR zXHkr*GtHiwfixM$mQ9o>#$g|nCU?lXi~PoYp|=bF*SXHvG1pJcie^1BMosEzNGufr zNeikCO4;+jlssW&O%CdyI~UY%;cxlU-IXczs08O9w!vs4{lN)L-;VRFlT`o~D}^$* zH`HJ$e>r9!eH5kazw5Y7FgtGl7UkN4=HZc^_d3+7a?olCZcohkjfE*KbINQC^P|r) zb^l*Kgn=97s;Dj?&N}!*?Qt$QM{~CKhfhvgSx{zcwXATP)hP8tgk2mfERsSss@ZYf zVrR;`t*FxJJ;j^$Jn%+j1s{_=Wh|OEoY+Mt#gseODJMdHo|6T}-ca2}$|tHb?x6?% z7$rI`r#b=q*r>L0W)FDqNB;UJS`_=Oh*>w?Ah&`PM>CNIzkdubr5Rz@3*-BoQR{m6 zUM$Tv+Vb1!m-zgo#~a_BX%#G&u+h2<(<{7Hw zh}v+#jD14qr=ByHI%Ua2R%xoSyxcy&FjBco@l+?ZIEd3VBOgb#wAJSU{&2+JnVwUk zk4sE*DD#0vO!WW0{k;eS$rC(KUMj=q#x?te?S$W&9#+zrfvW~`k8#%=-z&| zxixy{q)r)EZA^hS68(65? zKa&mq-+pjTu*H?V?~V>ABh;qK^J(Vo{9sPirZg- zze}Y>JI6-h0xyjJEoUp7h+tsZkXcPCrj&DuulQhKV&N2p9fY3E7*;j~morN2ip-PU zviAVV1w*6W*@oXcD&+U2V1A`*{h`xH3^O1WRdN~G6NU%9G%_Wt<|%9w|L_~x8f=@R zW~&k$?vE8_v|9~}>UOuT#`1s1P6pt;DSevKWZEA8+$7;EP1v=LnZX-F>endP3K^VJ z42TcaL=WpJS1#(zxG7qC1@&O7i~&+>=;F?GeFxLfLNYFl91fx?)zAxHtWe-z!Zia! zQFW5z3Ildl!E6pe%R@$%Cx!nWNBQk#K|FEsub&1EO(Q3EmGT?( zFN9RIlSJy|UJ$0yV01^3cXADTRJgKxLmZjFq*UQ>ji>CdL)7)c%i8xy+WOS-(1}Wa z0MLm4q$nJt5LC0fW{Sqam@5zv;rCyWS1BVA}% zpUv5$1lf~2Z#~g~AkSyE>8;VTmHT~aw)%_WVBlvf2fixGWm5Ss2^sq-0_4%;%N2gt z@W#9qDITc0zY_tB&3F6+_XY;?U~lZYjyH3y$ds07@nb(Um82=C*te0tSyi!S#(WO1 zR)x0;?vhKN>w{*7isBmY0L&~Z=3MpbL?goFOIX}lj$#8abZ8L$^kh3{E3&zL2*WQ8=?PO{F% zk=h{iQ-ivv0KwPNi5&AmbeGT)wM@Ij;$W(4j=1b_JnOl5x#!IAr9HmVGD!KAAGK_c z3Y+iss6DvIaa?FrJ*c9G=25tTi#|L<&A%Ws1YKv>{LAnRES6+WjV!q8?L!;KqDl0OrJks81BJmRw2-qSe607niWj}<7x>BZaH935m zoyDjM6?iAym}U(CkHsqeP7(KRb;rk_X~@Fw!s3+d6T!>Wa*pTMI?W-H56ud9%Xro5 zrV8f?<^uMmVFHcCD6b!|PudfpW}EHyRBYNl@c zjy=yczqn>pu#`e}4in5~85)c~c|ty4?nk1hwk9|vA7IjffuS)srW05mC!wdK6k8mP zfr+iSBN&CCK$5S%%IiiR7)qrR--{-FR^S&MZqqxvQ*0%{rt_!Dn&I*z*{rN`_+{(U zGi)p)Q9Wugys8>(dJN{egVX1>;?Ww(FGH!AQtOR~JlXL=Y*>x4Vfn=fj$o(#j0 z{{WVq-y~zN19{m#lmY6 zjFhgI)4bjia&KyF=ta{<%JQHmM0K4Er4xX%{PX|zY}%sj5^tL z|8%RXkN+z}w>Z&l!Lktu$C=4`&QTxU*HltF4}4{7<7{$_Q9T^V9l5^Q1!H)Xfpyu>bXklR@ynojqql9 zx5LSXET~;_0=OoQj)jZv)VEFyzi#hd=A&{;%SO1j%T$32MLXSCk~sSu_U4ttz1==3 zp=h}du&zy8KPH(HSEG(J*uxRmf!;+|fyBBiu+D1ra9E9dI<@~P9|zj|mq~LfP5iZR z!jlGkVbw||7I1vJT%}H#ExmL)2jpI(hsIlp)>2n1EVI*~O)KNKSRE|#OS+hCb}ra5 zUwibb|A&uB9yzH0j1!NPIl7d4Bj2^2tIqv-xmfjoabAXoSWkLDT~}dBN#@BjeTkZl zAgvo-sJK3V?5eI=G#b_=jgIC0z7NQr49z$=n{z$3|Df-Uf^h{8!`M$^B|MPJ= zPWzoj3Q6Z#0WO^f(qd!zYTWsOHtsB&SBydf#E{m#qqN&B{{#*LCAsF$ z#JrX<#+u7^#&>$jqP?+3tG7ZZ8w6|V!j?DZP)wYtW>?!OICJrNy+qZ$V^znM8$)q_ zn@gKxlUwo>T}bCIBNp21S_6|3VB^5yr&@Xw&_5p!B7$3k?5*H9GF+MC6n%_L`U(1!Vep1~})~0>`X@QRS_W&b` z#p)p}w#L-I-UffPHaC>>`F5?1XPW>D2=!Mc9%l;Vzfz;#qih|zZ?Vj{YlRR>1|#Y$uRG&ImuGvZq9 zn+?@Wp7&#*rFwl`7*G(hn0G#Q$n2j7*BN{6ReiR4@4k^WPA~DoybMbNRI~9TsNN>u zVYv7bm}Il>^dCdlp?LmYJ@IagI}1Oklf94U3@5+-;{$f1_F4rSz+U3c#w1>c&nyuaeFZ#UbfLGY75mOjH_yw$R*Y1NNfq_U{i>oFtFTS8; zXM0QLM*mcWZF)+Zi97vNSJx@LGL>lFbe|^t0!QTN+A-x>5l|P9HE-PTeY|T7{%ra7 z+R`j{JJoD-8UBRm9a(>@3%f=-3}ZNU1(I{jRl__ND2`fogziKoihj}LT3>!u_LBi2(keH%>V7Ho37ZeSIE(=f7s1~!TgJDe|x#l_Pl$J)pXpM$$X`my_ z8DGg;Ma+2eZpbaw?wfF#cR=pm8UsGDn4XdQI%zqLRFg0w0+U!UCC0W6eI4S@9{feX zPWx&`9R(u9YKn2UEHCTSM130a*!)N`fL)AXA9>#Pa9{o5nxl*k(P1VVCo{tvn#`M+ zLXqwqoBhjqF-BLglU0LEu9Q_qX__#NPO*Qx73Z#`Y$BJHvUYA&a+Ek$yJIKZf95@V z-5iHm#nH<|$EU*OCkg(9!qv03*mv;2O1VTLE^Km5@eNH`oaglnXUtJDwRY~jEKT|u z1&2wuXRn;eg?2FUVudlVeGz+t29H*sq;=#8!!!*Ka!q;M_ab`FGM5%biNybotyRsf zu68%ONR1&D{6f6aVVN=?DxatTG&|oth%_9HY`9FW({logp~gn`WLC171T@ard2e|AF$-kv1XQia4{()jjhsSl|9KG#Ku-$(Fv*_ z)wm#o{GeE209qD3)s+wB{#oi)+##&kv-cKVGQjnc z(AIUP4#FQgR0tU$^{^Y1cm;T;BsBb6z*N`W{9Mm}wuJ6DO05dxx!pn-n^9!=Kb5$$Q<*YW9q_KFw0^y>+)$MXM0SlMQ zGL4^9R>hS{zBts=o>a<9b8x_%2OQ04gyYzcjU2fEu~HmK$c3(qyx>53emMLkAp@XZ zoD^z;%MRWMKU#k@S0AtkZhMiAjl$w>1Lv^n#GZ_Fd67ull!>guC3c}))c=k4XFL7yAb7JeLT|1Z0JC*6qjevBZohCzZz8u5E)XA9V5bLyTxL!7y z_n>N-0RcnUsrP!@&#SS9L|V5uhT*4Ffmv6X275$B+@>K8;F>PnVOSV5LMt}kP?tq; zqu%W05)Z4ndm7xdCIzz&lndZ(4&Oz9Izu?}4xC&k#dL!xexyynYRYp{BC3dl;su*W z?OcKnjaf%8D7Z2pft%2N%&R!q7b?AnMjJ}qwxpy0?QBXhLKrIBT~zZO}Z z6Lzq*oozF~_aCyT&F5@o$T-9t8-o+3hssI_zDuGpD1p#OnxZh}l9z^ zFWEMlbBh|;gyXXPC~HhLaq~_s-&xyZwpp9E8f5GgdSZ{tCg*8{gF#}H-e%0ss$s!; z5P}8Veat7O!eak1^dkXJ?vWvw_}8BA?^oT~Hp~bPE=cxy21I%p)9cFv%M|T1>~Wsd z6D&ywbJ9K2C6=~xBVpuUwVjI^nW-Skv~jd#KUNuxGL^OQ%QA>fw|5a{b*>X4d``z( zXOLo?ZwoBtYme>h)4;=O;Y>MLj?9tlK|6}hy6X#=FVO=46aOLAisY`HF z5GCwBB9ygCNTB~rt%^pi#n8Akxd9)F&0UPZAqW5_p6-muP8h3kP??uggqB^D|4ebZDNcZj`hDxcoZot~=y2qBDTsVc+FLZ5kb%KNhyCV(-}HJWo>+ zH0T8Ye#LA`y4$u8Y3e&J6X$0oWUItY0g*TuA#tOmgIaMcE3nG0fj&b`@x)8B$>g|X zP15{kKBOd6ltshlQ;2TSG&W1Qzb4=>nD0q1At7azc+r^5=jM^|Ef!fPZbXS%bkI#} zYwDW&Qxwz~63vyox%F?s(Y^3iO+DpLuHHy2a~aFQm+I>u>=$!sg&&(|D8Lhj;RK2r z!Sxu$gJGdd&bF(YZ3PBval1VAp4#RpD%RkvdJ{VR*IeTZKko~W1y=WJE1}X9T8n>> zcUJ00TT;)Ie>en3KCh3Pb9Pq!#0)P1-~e3y>Rk>!aCruR5{;-O6T8Np`gHG8q{-a? zrnUaGrJqAW5(%kbj>e*{ToiUArr{OU-ttmxBSC099NZZf%CD*GO5hpE13U=9dV6jRils(R?LbpV`4kB)y@+Ju8Z{S@ zI>Z-sil6sz7_74@tyRk0Y3FXTpxc3eP-RmoaP5Uz%Jl@yq>W@g_}G~8Q+RceU&Sw~ zEBTc%5pD`IYkk)L82bKpi8DwxLPL{zXj-D!6J|v#^eFX_6CDuh*;1;kNM;MK7KHd2S%Q_=3@!!$=e-Q)r6AIq~@tSjXAEowkFqAy?Q(jY5b6Q=D+5n*lrRFV#i4; zy~7VJcU|^dWa%ZNbuDa~pzQNP{19d`)PF{VF7m1tHrUG2h1yK5ql2-G#l5^K0ib2pZHhlcq z6lRdApFLYtA67y&tLXAi%XFQ9)P}HB@hFEf$s};3Z|_O=zj7#4;yOwb+?x06L2T&7 zKP_;SAVDd@LjlxdO%e5hD4F01gdqu%f8>~?M4LPf6%&)u-F#0W9QMRVyt8l=8`hMw z>~A^INjX@OM4^IBMLTDtryATqgv?jKtd{PV=a$Z{Q2~^fb!XCZD5B$1DtvHQYkg5Q zRdYP3>S9HxJ@ulMA|%zmkr+iw-s~ROenc{u9+NDX*rQ3e7W>6kC37L-kBIpox2y~4 z38f?)8hLKutknxDPyFelGzX&5OaW9{X`_l1SJ|PZt_>nB-Vx3C0D+&!q2n+p( z*;f=xX%0yLGdX#Y+j;-%tc*fz51O{_m)VuwsD-3~v_&onx9(rIb1SdI-{{hw z@T7aE@EU#;`_HcEks;z?xVvn;r>5Y9CucH(^Mod9>x#GRL0L(53s#o-b+M;ZeUg2J zSzNV6Pr=n?2q4^Fj^sr8xA3i%Y^pVf9k{3k<_B2qKpd-%b;-g`cOlZ zOv;Z*OTiLHG!5$9@kO8|DycM^@e;lS>75Bj$FYJVFIU2_Jab}6hDy5K05(~fv+`PB zAxdK0i^H$-I~yl5<-knaAtJzoOQIL?NMuk;5*uqeRVXnT&`-6)9Z}8mb5JT zmx9Y#2i=MN(DNMPnwx$mUBlP`igT#J71&XM6*;pf`(84r)1oNl^<}M$3krM=aW^`c zv2S4e#P%J=p^m8@W(#9d#HWyDRHH=~aFXHZ;NfEa>`8MN5?PB7`$NP(~K1l;nGTIX^HCsxV(q4(m z8z-Nm43@Rr=|oS(k&%wwWF~vxK>*CE_bG~zHACrMWlQUj*M2|AE1qU}v!;x^AAd?L zS(6yUQeMcbwrI(Tb>*VPw4=7?j^A)B?_pibttK7fSZS!5HM^Q zkzVOOaOwS>T~-EoCW(Q;QEte|X?o)3MdeSi`H&rT==1(?Ps$CoSeyKg$R2+ zvbC#Ch?&s)>31m^flnV=+WthIHgHi6WM7JeC1c&Gw$ zp#}13Gxp6EnR*@tA5ohOBTGOBThZ;<)+F+U6n#P(bsE*4h1R(UhOy`Uk$lN??qh55 z_OYm)&#;8lHg^uGDee{=7v7;U4mXe?ol8BAihW;6y^LP0xatUO27h-ftnoLz53a34 z(Z5u^MKe+! z5Se$wwYdHPGQIyXbbx_p<=ca;?MON}EivlAenKu+ip$%xuuIs>L|c|ybWGtkNnVWHcdbfncFyu1<$`0FT^0dkK8Dim*FTE{-GH17RhxTGIh7)^m@yv?p z&%0hHACgy@e0qr2u0>!U-&!b64?SDL=JLW~Hh|S8C%pL6tYnl}(9350MCDAzqo_Dl z4X~fa6zbraO!sojTW1@BeHT-bzRN2u5{eQYV_i33*0fFD*sNfQY2m=5Ti{bd0;rKJ zrxiPfkd&Qz-C`^R8$q_IM7FPG(6L?ETw!N~Yht7Ymp8>$vR_SN-?!)mV&sxs2U+>#@zM-Jjztj;JDA z0=>5KhY@^Sw7E|wH87tM1FD#0X;~tZH2lCm!V)+eRcU(TtkLWgAvFy@@190@zoWo8 zN`_<^C8Piv)hNR_5=c?u5DuEwMFH|8K^5{5BR_N+(1{f9NseX8!RWFB&d;(MfD^Eo zlX6dXTVHYp`Wuo7=A;ExJ#Zg#qyu5feA(wbBAZ}irWl{u_$Hd>Eo*2#F zEX6l32OznM7Nq1X$%+4pNs@d%hdK<*ug>zQd&t%Tbitq8p(lYZyIzl49J9B}VLK_v zrBsf4RS#M8=zGFU8Ve+gg4CCKC0u&%vM zA2!Yv^nzNw^>Tu|>4B6xfDSc6%3KVilY+YenC5)(ss*B?KD^6D(6LHFk!iHfa}HJA zCB=62_vRlSBb9NkdCLL&Q7HS4c&dp>I{d<^MU6Twy8xnv_YF6;iRLlG1<4z2FZivD zguZ&{ek=*~rzdVe`0#%!C#!j_4iw}NhOn`Nv9Z``9g&vl(=-Whg&j_liwKiVam+pF zf@IXnz1u1DX%xXPQ>gA`O%ee(Kn?CdQ%Q?MoY&gwp=N@7QwpB4yqX3*)@hldjY zkm2Dz>Ze-?$w%eh%9b6|zfySNF#sJk@@k159nDCN$27<$%QB5D^^(YOUleHeRWri# zFF~Vb)COw*-tOpu{LKTP4*OuRs`@zwkG6{>1NMiD`yc*$rw!-@54y^JhRQ^Rqy$PL0ghsu8AA{PHFmK2ZT z=X=%|wn99#)yaQUH|N3v4f)i2SzZ1tvv^dM&;6~{c5&eZTE>0~; z5)&K6x;toneBm=?y~>KwGK?k*Kk1-uAt%j>RBtv^R@^4eBjRXG5KZkfW#_Z##3$(c$f4°QV?*(% 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
pGp4wiU|q+>b`D$`sg%MideV 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 literal 0 HcmV?d00001 diff --git a/public/uploads/.gitkeep b/public/uploads/.gitkeep new file mode 100644 index 0000000..1b22c42 --- /dev/null +++ b/public/uploads/.gitkeep @@ -0,0 +1,2 @@ +# This file ensures the uploads directory is created for logo uploads + diff --git a/public/uploads/canceled-order-notification.mp3 b/public/uploads/canceled-order-notification.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..fb10e018938e7ae3f88c872d89121f869fb37b67 GIT binary patch literal 33024 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%0ABS;uJ@105=rVF&p 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 z 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°QV?*(% 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
pGp4wiU|q+>b`D$`sg%MideV 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 literal 0 HcmV?d00001 diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..15b2820 --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,117 @@ +const config = require('../config'); +const apiClient = require('../api-client'); + +async function authRoutes(fastify, options) { + + // Root route - redirect based on signed session cookie validity + fastify.get('/', async (req, reply) => { + try { + const raw = req.cookies && req.cookies.kitchen_session; + if (raw) { + const { valid, value } = req.unsignCookie(raw || ''); + if (valid) { + const token = config.get('authToken'); + const expiry = config.get('tokenExpiry'); + if (token && !apiClient.isTokenExpired(expiry) && value === token) { + return reply.redirect('/dashboard'); + } + } + } + } catch (_) {} + return reply.redirect('/login'); + }); + + // Login page + fastify.get('/login', async (req, reply) => { + try { + const raw = req.cookies && req.cookies.kitchen_session; + if (raw) { + const { valid, value } = req.unsignCookie(raw || ''); + if (valid) { + const token = config.get('authToken'); + const expiry = config.get('tokenExpiry'); + if (token && !apiClient.isTokenExpired(expiry) && value === token) { + return reply.redirect('/dashboard'); + } + } + } + } catch (_) {} + return reply.view('login', { + error: req.query.error || null, + recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || '' + }); + }); + + // Login handler + fastify.post('/auth/login', async (req, reply) => { + const { login, password, 'g-recaptcha-response': recaptchaToken } = req.body; + + // Validate inputs + if (!login || !password) { + return reply.view('login', { + error: 'Please provide email and password', + recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || '' + }); + } + + if (!recaptchaToken) { + return reply.view('login', { + error: 'reCAPTCHA verification required', + recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || '' + }); + } + + try { + // Call API to authenticate + const result = await apiClient.login(login, password, recaptchaToken); + + if (result.error) { + return reply.view('login', { + error: result.message || 'Login failed', + recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || '' + }); + } + + // Store auth token and expiration + config.set('authToken', result.token); + config.set('tokenExpiry', result.expirationDate); + config.set('userEmail', login); + + // Set signed cookie for session; secure only on HTTPS + const isHttps = (req.protocol === 'https') || ((req.headers['x-forwarded-proto'] || '').toString().toLowerCase() === 'https'); + reply.setCookie('kitchen_session', result.token, { + signed: true, + httpOnly: true, + secure: isHttps, + sameSite: 'strict', + maxAge: 30 * 24 * 60 * 60, + path: '/' + }); + + return reply.redirect('/dashboard'); + + } catch (error) { + console.error('Login error:', error.message); + return reply.view('login', { + error: 'An error occurred during login. Please try again.', + recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || '' + }); + } + }); + + // Logout handler + fastify.post('/auth/logout', async (req, reply) => { + // Allow multiple sessions: only clear the browser cookie + reply.clearCookie('kitchen_session'); + return reply.redirect('/login'); + }); + + fastify.get('/auth/logout', async (req, reply) => { + // Allow multiple sessions: only clear the browser cookie + reply.clearCookie('kitchen_session'); + return reply.redirect('/login'); + }); +} + +module.exports = authRoutes; + diff --git a/routes/dashboard.js b/routes/dashboard.js new file mode 100644 index 0000000..60a1cf7 --- /dev/null +++ b/routes/dashboard.js @@ -0,0 +1,35 @@ +const config = require('../config'); +const database = require('../database'); + +// Middleware to check authentication via signed cookie +async function requireAuth(req, reply) { + const raw = req.cookies && req.cookies.kitchen_session; + if (!raw) { reply.redirect('/login'); return; } + const { valid, value } = req.unsignCookie(raw || ''); + if (!valid) { reply.redirect('/login'); return; } + const token = config.get('authToken'); + const expiry = config.get('tokenExpiry'); + const apiClient = require('../api-client'); + if (!token || apiClient.isTokenExpired(expiry) || value !== token) { + reply.redirect('/login'); + return; + } +} + +async function dashboardRoutes(fastify, options) { + + // Dashboard page + fastify.get('/dashboard', { preHandler: requireAuth }, async (req, reply) => { + const appConfig = config.getAll(); + const stats = database.getOrderStats(); + + return reply.view('dashboard', { + config: appConfig, + stats: stats, + showStats: appConfig.showOrderStats !== 'false' + }); + }); +} + +module.exports = dashboardRoutes; + diff --git a/routes/orders.js b/routes/orders.js new file mode 100644 index 0000000..44befed --- /dev/null +++ b/routes/orders.js @@ -0,0 +1,343 @@ +const config = require('../config'); +const database = require('../database'); +const apiClient = require('../api-client'); +const printer = require('../printer'); + +// Middleware to check authentication via signed cookie (JSON response) +async function requireAuth(req, reply) { + const raw = req.cookies && req.cookies.kitchen_session; + if (!raw) { return reply.code(401).send({ error: true, message: 'Not authenticated' }); } + const { valid, value } = req.unsignCookie(raw || ''); + if (!valid) { return reply.code(401).send({ error: true, message: 'Not authenticated' }); } + const token = config.get('authToken'); + const expiry = config.get('tokenExpiry'); + const apiClient = require('../api-client'); + if (!token || apiClient.isTokenExpired(expiry) || value !== token) { + return reply.code(401).send({ error: true, message: 'Not authenticated' }); + } +} + +async function ordersRoutes(fastify, options) { + + // Get orders with filters + fastify.get('/api/orders', { preHandler: requireAuth }, async (req, reply) => { + const filters = { + status: req.query.status, + limit: parseInt(req.query.limit, 10) || 50 + }; + + // Get today's date for stats + const today = new Date(); + filters.date = today; + + const orders = database.getOrders(filters); + const stats = database.getOrderStats(); + + return { error: false, orders, stats }; + }); + + // Update order status + fastify.post('/api/orders/:id/status', { preHandler: requireAuth }, async (req, reply) => { + const orderId = parseInt(req.params.id, 10); + const { status } = req.body; + + if (!status) { + return { error: true, message: 'Status is required' }; + } + + // Valid local statuses + const validStatuses = ['new', 'preparing', 'ready', 'completed']; + if (!validStatuses.includes(status)) { + return { error: true, message: 'Invalid status' }; + } + + try { + // Update local database + database.updateOrderStatus(orderId, status); + + // Sync to backend if status is completed (maps to finished) + if (status === 'completed') { + const appConfig = config.getAll(); + const order = database.getOrderById(orderId); + + if (order && appConfig.authToken && appConfig.selectedBotId) { + // Determine the action based on order type + let action = 'finished'; + if (order.order.type === 'delivery') { + action = 'delivered'; + } else if (order.order.type === 'pickup') { + action = 'picked_up'; + } + + const result = await apiClient.modifyOrder( + appConfig.authToken, + appConfig.selectedBotId, + orderId, + action + ); + + if (result.error) { + console.error('Failed to sync order status to backend:', result.message); + } + } + } + + return { error: false }; + } catch (error) { + console.error('Failed to update order status:', error.message); + return { error: true, message: 'Failed to update order status' }; + } + }); + + // Cancel order + fastify.post('/api/orders/:id/cancel', { preHandler: requireAuth }, async (req, reply) => { + const orderId = parseInt(req.params.id, 10); + const { reason } = req.body; + + try { + // Check if cancellation already printed to prevent duplicates + if (database.hasPrintedCancellation(orderId)) { + console.log(`[API] Cancellation already printed for order #${orderId}, skipping duplicate`); + database.updateOrderStatus(orderId, 'canceled'); + return { error: false, message: 'Order already canceled' }; + } + + // Update local database + database.updateOrderStatus(orderId, 'canceled'); + + // Sync to backend + const appConfig = config.getAll(); + if (appConfig.authToken && appConfig.selectedBotId) { + const result = await apiClient.modifyOrder( + appConfig.authToken, + appConfig.selectedBotId, + orderId, + 'cancel', + reason || 'Canceled by kitchen' + ); + + if (result.error) { + console.error('Failed to sync cancellation to backend:', result.message); + } + } + + // Print cancellation receipt + const order = database.getOrderById(orderId); + if (order) { + try { + // Add to print queue with deduplication check + const jobId = database.addToPrintQueue(orderId, 'canceled'); + if (!jobId) { + console.log(`[API] Cancellation print job not created (duplicate prevention) for order #${orderId}`); + return { error: false, message: 'Cancellation recorded' }; + } + + database.markPrintJobProcessing(jobId); + + // Get enabled printers from database + const printerConfigs = database.getEnabledPrinters(); + let result; + + if (printerConfigs && printerConfigs.length > 0) { + // Use new per-printer config system + result = await printer.printOrderReceiptWithPrinterConfigs( + order, + printerConfigs, + 'canceled', + { reason: reason || 'Canceled by kitchen' } + ); + } else { + // Fallback to legacy system if no printer configs + if (!printer.printer) { + printer.initializePrinter(appConfig); + } + result = await printer.printOrderReceipt(order, 'canceled', { reason: reason || 'Canceled by kitchen' }); + } + + if (result && result.success) { + database.markOrderPrinted(orderId); + database.markPrintJobCompleted(jobId); + // Cleanup any other pending jobs for this order+type + database.cleanupDuplicateJobs(jobId, orderId, 'canceled'); + } else { + // Mark as pending for worker retry + database.markPrintJobPending(jobId); + } + } catch (error) { + console.error('Failed to print cancellation receipt:', error.message); + // Let the worker retry - find the job and mark it pending + try { + const lastJobIdRow = database.db.prepare("SELECT id FROM print_queue WHERE order_id = ? AND print_type = 'canceled' ORDER BY id DESC LIMIT 1").get(orderId); + if (lastJobIdRow && lastJobIdRow.id) { database.markPrintJobPending(lastJobIdRow.id); } + } catch (_) {} + } + } + + return { error: false }; + } catch (error) { + console.error('Failed to cancel order:', error.message); + return { error: true, message: 'Failed to cancel order' }; + } + }); + + // Reprint order + fastify.post('/api/orders/:id/reprint', { preHandler: requireAuth }, async (req, reply) => { + const orderId = parseInt(req.params.id, 10); + + try { + const order = database.getOrderById(orderId); + + if (!order) { + return { error: true, message: 'Order not found' }; + } + + const printType = order.localStatus === 'canceled' ? 'canceled' : 'new'; + + // Check for recent ACTIVE job to prevent double-enqueue while in-flight + const activeCheck = database.hasActiveOrRecentJob(orderId, 'reprint', 10); + if (activeCheck.hasActive && (activeCheck.status === 'pending' || activeCheck.status === 'processing')) { + console.log(`[API] Reprint request for order #${orderId} blocked - active job ${activeCheck.jobId} (${activeCheck.status}) exists`); + return { error: false, message: 'Print already in progress' }; + } + + // Add to print queue with deduplication + const jobId = database.addToPrintQueue(orderId, 'reprint'); + if (!jobId) { + console.log(`[API] Reprint job not created (duplicate prevention) for order #${orderId}`); + return { error: false, message: 'Print recently completed, skipping duplicate' }; + } + + database.markPrintJobProcessing(jobId); + + // Print receipt using per-printer configs + const printerConfigs = database.getEnabledPrinters(); + + let result; + if (printerConfigs && printerConfigs.length > 0) { + // Use new per-printer config system + result = await printer.printOrderReceiptWithPrinterConfigs(order, printerConfigs, printType, { cooldownMs: 2000 }); + } else { + // Fallback to legacy system + const appConfig = config.getAll(); + printer.initializePrinter(appConfig); + result = await printer.printOrderReceipt(order, printType, { cooldownMs: 2000 }); + } + + if (result && result.success) { + database.markOrderPrinted(orderId); + database.markPrintJobCompleted(jobId); + // Cleanup any other pending jobs for this order+type + database.cleanupDuplicateJobs(jobId, orderId, 'reprint'); + return { error: false, message: (result && result.message) ? result.message : 'Receipt sent to printer' }; + } else { + // Mark as pending so the worker can retry when printer is online + database.markPrintJobPending(jobId); + return { error: true, message: result.error || 'Print failed' }; + } + } catch (error) { + console.error('Failed to reprint order:', error.message); + try { database.resetStuckProcessingJobs(60); } catch (_) {} + return { error: true, message: 'Failed to reprint order' }; + } + }); + + // Get single order details + fastify.get('/api/orders/:id', { preHandler: requireAuth }, async (req, reply) => { + const orderId = parseInt(req.params.id, 10); + const order = database.getOrderById(orderId); + + if (!order) { + return { error: true, message: 'Order not found' }; + } + + return { error: false, order }; + }); + + // Manual sync trigger (for testing/debugging) + fastify.post('/api/sync-now', { preHandler: requireAuth }, async (req, reply) => { + try { + // Trigger the poller manually + if (fastify.orderPoller) { + fastify.orderPoller.poll(); + return { error: false, message: 'Manual sync triggered' }; + } else { + return { error: true, message: 'Order poller not available' }; + } + } catch (error) { + console.error('Manual sync error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Health check for external API server + fastify.get('/api/health/external', { preHandler: requireAuth }, async (req, reply) => { + try { + const appConfig = config.getAll(); + const token = appConfig.authToken; + const botId = appConfig.selectedBotId; + + if (!token || !botId) { + return { + error: false, + status: 'unconfigured', + message: 'API not configured', + timestamp: new Date().toISOString() + }; + } + + // Check if token is expired + if (apiClient.isTokenExpired(appConfig.tokenExpiry)) { + return { + error: false, + status: 'offline', + message: 'Token expired', + timestamp: new Date().toISOString() + }; + } + + // Try to fetch bots list as a lightweight health check + const startTime = Date.now(); + const result = await apiClient.getBots(token); + const responseTime = Date.now() - startTime; + + if (result.error) { + return { + error: false, + status: 'offline', + message: result.message || 'API server unreachable', + responseTime: responseTime, + timestamp: new Date().toISOString() + }; + } + + return { + error: false, + status: 'online', + message: 'API server connected', + responseTime: responseTime, + timestamp: new Date().toISOString() + }; + } catch (error) { + console.error('External API health check error:', error.message); + return { + error: false, + status: 'offline', + message: error.message || 'Health check failed', + timestamp: new Date().toISOString() + }; + } + }); + + // Health check for local dashboard server + fastify.get('/api/health/local', { preHandler: requireAuth }, async (req, reply) => { + return { + error: false, + status: 'online', + message: 'Local server connected', + timestamp: new Date().toISOString() + }; + }); +} + +module.exports = ordersRoutes; + diff --git a/routes/settings.js b/routes/settings.js new file mode 100644 index 0000000..0eb8282 --- /dev/null +++ b/routes/settings.js @@ -0,0 +1,459 @@ +const config = require('../config'); +const database = require('../database'); +const apiClient = require('../api-client'); +const printer = require('../printer'); +const path = require('path'); +const fs = require('fs'); +const { pipeline } = require('stream/promises'); + +// Middleware to check authentication via signed cookie +async function requireAuth(req, reply) { + const raw = req.cookies && req.cookies.kitchen_session; + if (!raw) { reply.redirect('/login'); return; } + const { valid, value } = req.unsignCookie(raw || ''); + if (!valid) { reply.redirect('/login'); return; } + const token = config.get('authToken'); + const expiry = config.get('tokenExpiry'); + const apiClient = require('../api-client'); + if (!token || apiClient.isTokenExpired(expiry) || value !== token) { + reply.redirect('/login'); + return; + } +} + +async function settingsRoutes(fastify, options) { + + // Settings page + fastify.get('/settings', { preHandler: requireAuth }, async (req, reply) => { + const appConfig = config.getAll(); + + // Fetch available bots + let bots = []; + if (appConfig.authToken) { + const botsResult = await apiClient.getBots(appConfig.authToken); + if (!botsResult.error && botsResult.bots) { + bots = botsResult.bots; + } + } + + return reply.view('settings', { + config: appConfig, + bots: bots, + message: req.query.message || null, + error: req.query.error || null + }); + }); + + // Save settings + fastify.post('/settings/save', { preHandler: requireAuth }, async (req, reply) => { + try { + const { + selectedBotId, + pollingInterval, + dashboardRefreshInterval, + showOrderStats, + soundNotificationsEnabled, + soundVolume, + printerType, + printerInterface, + printerPath, + printerWidth, + fontSize, + lineStyle, + qrCodeEnabled, + qrCodeSize, + qrCodeCorrection, + qrCodeContentTemplate, + headerText, + footerText, + businessName, + businessAddress, + businessPhone, + businessWebsite, + businessEmail, + businessContactSize, + showCustomerInfo, + showOrderItems, + showPrices, + showTimestamps, + selectedPrintersJson + } = req.body; + + // Validate and save configuration + const configToSave = {}; + + if (selectedBotId) configToSave.selectedBotId = selectedBotId; + if (pollingInterval) configToSave.pollingInterval = pollingInterval; + if (dashboardRefreshInterval) configToSave.dashboardRefreshInterval = dashboardRefreshInterval; + + configToSave.showOrderStats = showOrderStats === 'on' ? 'true' : 'false'; + + // Sound notification settings + configToSave.soundNotificationsEnabled = soundNotificationsEnabled === 'on' ? 'true' : 'false'; + if (soundVolume !== undefined) configToSave.soundVolume = soundVolume; + + if (printerType) configToSave.printerType = printerType; + if (printerInterface) configToSave.printerInterface = printerInterface; + if (printerPath) configToSave.printerPath = printerPath; + if (printerWidth) configToSave.printerWidth = printerWidth; + if (fontSize) configToSave.fontSize = fontSize; + if (lineStyle) configToSave.lineStyle = lineStyle; + + configToSave.qrCodeEnabled = qrCodeEnabled === 'on' ? 'true' : 'false'; + if (qrCodeSize) configToSave.qrCodeSize = parseInt(qrCodeSize, 10); + if (qrCodeCorrection) configToSave.qrCodeCorrection = qrCodeCorrection; + if (qrCodeContentTemplate !== undefined) configToSave.qrCodeContentTemplate = qrCodeContentTemplate; + + if (headerText !== undefined) configToSave.headerText = headerText; + if (footerText !== undefined) configToSave.footerText = footerText; + if (businessName !== undefined) configToSave.businessName = businessName; + if (businessAddress !== undefined) configToSave.businessAddress = businessAddress; + if (businessPhone !== undefined) configToSave.businessPhone = businessPhone; + if (businessWebsite !== undefined) configToSave.businessWebsite = businessWebsite; + if (businessEmail !== undefined) configToSave.businessEmail = businessEmail; + if (businessContactSize) configToSave.businessContactSize = businessContactSize; + + configToSave.showCustomerInfo = showCustomerInfo === 'on' ? 'true' : 'false'; + configToSave.showOrderItems = showOrderItems === 'on' ? 'true' : 'false'; + configToSave.showPrices = showPrices === 'on' ? 'true' : 'false'; + configToSave.showTimestamps = showTimestamps === 'on' ? 'true' : 'false'; + + // Multi-printer selection (stored raw JSON string) + // Smart sync: If multi-printer list is empty and main printer is configured, + // auto-populate multi-printer with main printer to avoid confusion + if (selectedPrintersJson !== undefined) { + try { + let parsed = JSON.parse(selectedPrintersJson || '[]'); + if (!Array.isArray(parsed)) parsed = []; + + // If multi-printer list is empty but main printer is configured, + // auto-add the main printer to the multi-printer list + if (parsed.length === 0 && printerInterface && printerPath) { + const mainType = printerInterface === 'serial' ? 'com' : printerInterface; + parsed.push({ + type: mainType, + interface: printerPath + }); + console.log('[Settings] Auto-populated multi-printer list with main printer:', mainType, printerPath); + } + + configToSave.selectedPrintersJson = JSON.stringify(parsed); + } catch (e) { + console.warn('[Settings] Failed to process selectedPrintersJson:', e.message); + // ignore malformed input; keep previous config + } + } + + // Save to database + config.setMultiple(configToSave); + + // Reinitialize printer with new config + const updatedConfig = config.getAll(); + printer.initializePrinter(updatedConfig); + + return reply.redirect('/settings?message=Settings saved successfully'); + } catch (error) { + console.error('Failed to save settings:', error.message); + return reply.redirect('/settings?error=Failed to save settings'); + } + }); + + // Test printer + fastify.post('/settings/test-printer', { preHandler: requireAuth }, async (req, reply) => { + try { + const appConfig = config.getAll(); + + // Allow ad-hoc test using current unsaved selections from UI + if (req.body && typeof req.body.selectedPrintersJson !== 'undefined') { + try { + const parsed = JSON.parse(req.body.selectedPrintersJson || '[]'); + if (Array.isArray(parsed)) { + appConfig.selectedPrintersJson = JSON.stringify(parsed); + } + } catch (_) { + // Ignore bad input for test; fall back to saved config + } + } + + // Always initialize to ensure latest selections are used + printer.initializePrinter(appConfig); + + const result = await printer.testPrint(); + + if (result.success) { + return { error: false, message: 'Test print successful' }; + } else { + return { error: true, message: result.error || 'Test print failed' }; + } + } catch (error) { + console.error('Test print error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Detect available printers and COM ports (hardware detection) + fastify.get('/api/printers/detect', { preHandler: requireAuth }, async (req, reply) => { + try { + const list = await printer.getAvailablePrinters(); + return { error: false, printers: list }; + } catch (error) { + console.error('List printers error:', error.message); + return { error: true, message: error.message, printers: [] }; + } + }); + + // Get all configured printers from database + fastify.get('/api/printers/list', { preHandler: requireAuth }, async (req, reply) => { + try { + const printers = database.getAllPrinters(); + return { error: false, printers }; + } catch (error) { + console.error('Get printers error:', error.message); + return { error: true, message: error.message, printers: [] }; + } + }); + + // Get single printer configuration + fastify.get('/api/printers/:id', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + const printerConfig = database.getPrinter(printerId); + if (!printerConfig) { + return { error: true, message: 'Printer not found' }; + } + return { error: false, printer: printerConfig }; + } catch (error) { + console.error('Get printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Create new printer configuration + fastify.post('/api/printers/create', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = database.addPrinter(req.body); + const newPrinter = database.getPrinter(printerId); + return { error: false, message: 'Printer created successfully', printer: newPrinter }; + } catch (error) { + console.error('Create printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Update printer configuration + fastify.put('/api/printers/:id', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + database.updatePrinter(printerId, req.body); + const updatedPrinter = database.getPrinter(printerId); + return { error: false, message: 'Printer updated successfully', printer: updatedPrinter }; + } catch (error) { + console.error('Update printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Delete printer configuration + fastify.delete('/api/printers/:id', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + database.deletePrinter(printerId); + return { error: false, message: 'Printer deleted successfully' }; + } catch (error) { + console.error('Delete printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Set printer as default + fastify.post('/api/printers/:id/set-default', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + database.setDefaultPrinter(printerId); + return { error: false, message: 'Default printer updated' }; + } catch (error) { + console.error('Set default printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Toggle printer enabled/disabled + fastify.post('/api/printers/:id/toggle-enabled', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + const result = database.togglePrinterEnabled(printerId); + return { error: false, message: 'Printer status updated', is_enabled: result.is_enabled }; + } catch (error) { + console.error('Toggle printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Test specific printer + fastify.post('/api/printers/:id/test', { preHandler: requireAuth }, async (req, reply) => { + try { + const printerId = parseInt(req.params.id, 10); + const printerConfig = database.getPrinter(printerId); + + if (!printerConfig) { + return { error: true, message: 'Printer not found' }; + } + + const result = await printer.testPrintWithConfig(printerConfig); + + if (result.success) { + return { error: false, message: result.message || 'Test print successful' }; + } else { + return { error: true, message: result.error || 'Test print failed' }; + } + } catch (error) { + console.error('Test printer error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Upload logo - requires @fastify/multipart (now supports per-printer logos) + fastify.post('/settings/upload-logo', { preHandler: requireAuth }, async (req, reply) => { + try { + // Get multipart data + const data = await req.file(); + + if (!data) { + return { error: true, message: 'No file uploaded' }; + } + + // Ensure uploads directory exists + const uploadsDir = path.join(__dirname, '..', 'public', 'uploads'); + if (!fs.existsSync(uploadsDir)) { + fs.mkdirSync(uploadsDir, { recursive: true }); + } + + // Save file + const filename = `logo-${Date.now()}${path.extname(data.filename)}`; + const filepath = path.join(uploadsDir, filename); + + // Write file using stream + await pipeline(data.file, fs.createWriteStream(filepath)); + + // If printer_id is provided in fields, update that printer's logo + // Otherwise update global config (backward compatibility) + const fields = data.fields; + const printerId = fields && fields.printer_id ? parseInt(fields.printer_id.value, 10) : null; + + if (printerId) { + // Update specific printer's logo + const printerConfig = database.getPrinter(printerId); + if (printerConfig) { + database.updatePrinter(printerId, { ...printerConfig, logo_path: filepath }); + } + } else { + // Update global config (backward compatibility) + config.set('logoPath', filepath); + try { + const updated = config.getAll(); + printer.initializePrinter(updated); + } catch (e) { + // proceed even if reinit fails + } + } + + return { error: false, message: 'Logo uploaded successfully', path: `/public/uploads/${filename}`, filepath }; + } catch (error) { + console.error('Logo upload error:', error.message); + return { error: true, message: error.message }; + } + }); + + // Get available bots + fastify.get('/api/bots', { preHandler: requireAuth }, async (req, reply) => { + const appConfig = config.getAll(); + + if (!appConfig.authToken) { + return { error: true, message: 'Not authenticated' }; + } + + const result = await apiClient.getBots(appConfig.authToken); + + if (result.error) { + return { error: true, message: result.message }; + } + + return { error: false, bots: result.bots || [] }; + }); + + // Get notification settings (for dashboard) + fastify.get('/api/notification-settings', async (req, reply) => { + try { + const appConfig = config.getAll(); + return { + error: false, + soundNotificationsEnabled: appConfig.soundNotificationsEnabled || 'true', + soundVolume: appConfig.soundVolume || '80', + newOrderSoundPath: appConfig.newOrderSoundPath || '/public/sounds/new-order-notification.mp3', + canceledOrderSoundPath: appConfig.canceledOrderSoundPath || '/public/sounds/canceled-order-notification.mp3' + }; + } catch (error) { + console.error('Failed to get notification settings:', error.message); + return { error: true, message: error.message }; + } + }); + + // Upload sound file for notifications + fastify.post('/settings/upload-sound', { preHandler: requireAuth }, async (req, reply) => { + try { + // Get multipart data + const data = await req.file(); + + if (!data) { + return { error: true, message: 'No file uploaded' }; + } + + // Ensure sounds directory exists + const soundsDir = path.join(__dirname, '..', 'public', 'sounds'); + if (!fs.existsSync(soundsDir)) { + fs.mkdirSync(soundsDir, { recursive: true }); + } + + // Validate file type + const validExtensions = ['.mp3', '.wav', '.ogg']; + const fileExt = path.extname(data.filename).toLowerCase(); + + if (!validExtensions.includes(fileExt)) { + return { error: true, message: 'Invalid file type. Please upload MP3, WAV, or OGG file.' }; + } + + // Save file + const filename = `notification-${Date.now()}${fileExt}`; + const filepath = path.join(soundsDir, filename); + + // Write file using stream + await pipeline(data.file, fs.createWriteStream(filepath)); + + // Get the sound type from fields (newOrder or canceled) + const fields = data.fields; + const soundType = fields && fields.soundType ? fields.soundType.value : null; + + const publicPath = `/public/sounds/${filename}`; + + // Update config based on sound type + if (soundType === 'newOrder') { + config.set('newOrderSoundPath', publicPath); + } else if (soundType === 'canceled') { + config.set('canceledOrderSoundPath', publicPath); + } + + return { + error: false, + message: 'Sound uploaded successfully', + path: publicPath, + soundType: soundType + }; + } catch (error) { + console.error('Sound upload error:', error.message); + return { error: true, message: error.message }; + } + }); +} + +module.exports = settingsRoutes; + diff --git a/server.js b/server.js new file mode 100644 index 0000000..e7bae9a --- /dev/null +++ b/server.js @@ -0,0 +1,442 @@ +// Load environment variables +require('dotenv').config(); +const { checkAndUpdate } = require('./updater'); + +const Fastify = require('fastify'); +const path = require('path'); +const database = require('./database'); +const config = require('./config'); +const apiClient = require('./api-client'); +const printer = require('./printer'); +const PrintQueueWorker = require('./print-worker'); + +const fastify = Fastify({ + logger: true +}); + +const isDev = false; + +// Order Poller Class +class OrderPoller { + constructor(apiClient, database, printer) { + this.apiClient = apiClient; + this.db = database; + this.printer = printer; + this.intervalId = null; + this.isPolling = false; + } + + async start() { + console.log('Starting order poller...'); + + // Initial poll + this.poll(); + + // Schedule recurring polls + this.scheduleNextPoll(); + } + + scheduleNextPoll() { + const appConfig = config.getAll(); + const interval = parseInt(appConfig.pollingInterval, 10) || 15000; + + if (this.intervalId) { + clearTimeout(this.intervalId); + } + + this.intervalId = setTimeout(() => { + this.poll(); + this.scheduleNextPoll(); + }, interval); + } + + async poll() { + if (this.isPolling) { + console.log('Poll already in progress, skipping...'); + return; + } + + this.isPolling = true; + + try { + const appConfig = config.getAll(); + + // Check if configured + if (!appConfig.authToken || !appConfig.selectedBotId) { + console.log('Not configured yet, skipping poll'); + this.isPolling = false; + return; + } + + // Check if token is expired + if (apiClient.isTokenExpired(appConfig.tokenExpiry)) { + console.log('Token expired, please re-login'); + this.isPolling = false; + return; + } + + // Get last synced order ID + const lastOrder = this.db.getLastOrder(); + const afterId = lastOrder ? lastOrder.order_id : 0; + + console.log(`Polling for new orders (afterId: ${afterId})...`); + + // Fetch new orders from API + const result = await this.apiClient.getOrders( + appConfig.authToken, + appConfig.selectedBotId, + afterId, + { includeCanceled: true, limit: 50 } + ); + + if (result.error) { + console.error('Failed to fetch orders:', result.message); + this.isPolling = false; + return; + } + + const orders = result.orders || []; + console.log(`Received ${orders.length} orders from API`); + + // Process each order + for (const order of orders) { + const existingOrder = this.db.getOrderById(order.id); + + if (!existingOrder) { + // New order - save and print + console.log(`New order detected: #${order.id}`); + this.db.insertOrder(order); + + // Initialize printer if needed + if (!this.printer.printer) { + this.printer.initializePrinter(appConfig); + } + + // Add to print queue with deduplication check + const newJobId = this.db.addToPrintQueue(order.id, 'new'); + if (!newJobId) { + console.log(`Print job not created for order #${order.id} (duplicate prevention)`); + continue; + } + + this.db.markPrintJobProcessing(newJobId); + + try { + // Use per-printer config system + const printerConfigs = this.db.getEnabledPrinters(); + let result; + + if (printerConfigs && printerConfigs.length > 0) { + result = await this.printer.printOrderReceiptWithPrinterConfigs(order, printerConfigs, 'new'); + } else { + // Fallback to legacy system + if (!this.printer.printer || !this.printer.config) { + const currentConfig = require('./config').getAll(); + this.printer.initializePrinter(currentConfig); + } + result = await this.printer.printOrderReceipt(order, 'new'); + } + + if (result && result.success) { + this.db.markOrderPrinted(order.id); + this.db.markPrintJobCompleted(newJobId); + // Cleanup any other pending jobs for this order+type + this.db.cleanupDuplicateJobs(newJobId, order.id, 'new'); + console.log(`✓ Receipt printed for order #${order.id}`); + } else { + console.error(`✗ Print result indicates failure for order #${order.id}:`, result && result.error ? result.error : 'Unknown error'); + this.db.markPrintJobPending(newJobId); + } + } catch (error) { + console.error(`✗ Failed to print order #${order.id}:`, error.message); + console.log(' Order saved to database. You can reprint from dashboard.'); + this.db.markPrintJobPending(newJobId); + } + } else { + // Check for status changes (cancellation status from backend) + const statusChanged = existingOrder.status !== order.status; + + if (statusChanged) { + console.log(`Order #${order.id} status changed: ${existingOrder.status} → ${order.status}`); + + this.db.updateOrder(order); + + // Print cancellation receipt if order was canceled + if (order.status === 'canceled' && existingOrder.status !== 'canceled') { + console.log(`Order #${order.id} was canceled - printing cancellation receipt...`); + + // Update local status to 'canceled' so it shows on the dashboard + this.db.updateOrderStatus(order.id, 'canceled'); + + // Check if cancellation already printed to prevent duplicates + if (this.db.hasPrintedCancellation(order.id)) { + console.log(`Skipping duplicate cancellation print for order #${order.id}`); + } else { + // Add to print queue with deduplication check + const jobId = this.db.addToPrintQueue(order.id, 'canceled'); + if (!jobId) { + console.log(`Cancellation job not created for order #${order.id} (duplicate prevention)`); + } else { + this.db.markPrintJobProcessing(jobId); + + try { + const cancelReason = order.cancellationReason || order.order?.cancellationReason || 'Order canceled'; + + // Use per-printer config system + const printerConfigs = this.db.getEnabledPrinters(); + let result; + + if (printerConfigs && printerConfigs.length > 0) { + result = await this.printer.printOrderReceiptWithPrinterConfigs(order, printerConfigs, 'canceled', { reason: cancelReason }); + } else { + // Fallback to legacy system + if (!this.printer.printer || !this.printer.config) { + const currentConfig = require('./config').getAll(); + this.printer.initializePrinter(currentConfig); + } + result = await this.printer.printOrderReceipt(order, 'canceled', { reason: cancelReason }); + } + + if (result && result.success) { + this.db.markOrderPrinted(order.id); + this.db.markPrintJobCompleted(jobId); + // Cleanup any other pending jobs for this order+type + this.db.cleanupDuplicateJobs(jobId, order.id, 'canceled'); + console.log(`✓ Cancellation receipt printed for order #${order.id}`); + } else { + console.error(`✗ Cancellation print failed for order #${order.id}:`, result && result.error ? result.error : 'Unknown error'); + this.db.markPrintJobPending(jobId); + } + } catch (error) { + console.error(`✗ Failed to print cancellation for order #${order.id}:`, error.message); + console.log(' Order updated in database. You can reprint from dashboard.'); + this.db.markPrintJobPending(jobId); + } + } + } + } + } + } + } + + // Reconciliation fetch: scan recent orders window to catch updates (e.g., cancellations) + try { + const recentResult = await this.apiClient.getOrders( + appConfig.authToken, + appConfig.selectedBotId, + 0, + { includeCanceled: true, limit: 200 } + ); + + if (!recentResult.error) { + const recentOrders = recentResult.orders || []; + for (const order of recentOrders) { + const existingOrder = this.db.getOrderById(order.id); + if (!existingOrder) { + // Skip new orders here; they are handled by the main afterId fetch + continue; + } + + // Check for status changes (cancellation status from backend) + const statusChanged = existingOrder.status !== order.status; + + if (statusChanged) { + console.log(`(reconcile) Order #${order.id} status changed: ${existingOrder.status} → ${order.status}`); + + this.db.updateOrder(order); + + // Print cancellation receipt if order was canceled + if (order.status === 'canceled' && existingOrder.status !== 'canceled') { + console.log(`(reconcile) Order #${order.id} was canceled - printing cancellation receipt...`); + + // Update local status to 'canceled' so it shows on the dashboard + this.db.updateOrderStatus(order.id, 'canceled'); + + // Check if cancellation already printed to prevent duplicates + if (this.db.hasPrintedCancellation(order.id)) { + console.log(`(reconcile) Skipping duplicate cancellation print for order #${order.id}`); + } else { + // Add to print queue with deduplication check + const jobId = this.db.addToPrintQueue(order.id, 'canceled'); + if (!jobId) { + console.log(`(reconcile) Cancellation job not created for order #${order.id} (duplicate prevention)`); + } else { + this.db.markPrintJobProcessing(jobId); + + try { + const cancelReason = order.cancellationReason || order.order?.cancellationReason || 'Order canceled'; + + // Use per-printer config system + const printerConfigs = this.db.getEnabledPrinters(); + let result; + + if (printerConfigs && printerConfigs.length > 0) { + result = await this.printer.printOrderReceiptWithPrinterConfigs(order, printerConfigs, 'canceled', { reason: cancelReason }); + } else { + // Fallback to legacy system + if (!this.printer.printer || !this.printer.config) { + const currentConfig = require('./config').getAll(); + this.printer.initializePrinter(currentConfig); + } + result = await this.printer.printOrderReceipt(order, 'canceled', { reason: cancelReason }); + } + + if (result && result.success) { + this.db.markOrderPrinted(order.id); + this.db.markPrintJobCompleted(jobId); + // Cleanup any other pending jobs for this order+type + this.db.cleanupDuplicateJobs(jobId, order.id, 'canceled'); + console.log(`✓ Cancellation receipt printed for order #${order.id}`); + } else { + console.error(`✗ Cancellation print failed for order #${order.id}:`, result && result.error ? result.error : 'Unknown error'); + this.db.markPrintJobPending(jobId); + } + } catch (error) { + console.error(`✗ Failed to print cancellation for order #${order.id}:`, error.message); + console.log(' Order updated in database. You can reprint from dashboard.'); + this.db.markPrintJobPending(jobId); + } + } + } + } + } + } + } + } catch (reconcileErr) { + console.error('Reconciliation fetch error:', reconcileErr.message); + } + + } catch (error) { + console.error('Poll error:', error.message); + } + + this.isPolling = false; + } + + stop() { + if (this.intervalId) { + clearTimeout(this.intervalId); + this.intervalId = null; + } + console.log('Order poller stopped'); + } + + restart() { + this.stop(); + this.start(); + } +} + +// Main initialization +async function main() { + try { + // Initialize database + database.init(); + console.log('Database initialized'); + + // Register plugins + await fastify.register(require('@fastify/view'), { + engine: { + ejs: require('ejs') + }, + root: path.join(__dirname, 'views') + }); + + await fastify.register(require('@fastify/static'), { + root: path.join(__dirname, 'public'), + prefix: '/public/' + }); + + await fastify.register(require('@fastify/cookie'), { + secret: process.env.COOKIE_SECRET || 'kitchen-agent-secret-key-change-in-production', + hook: 'onRequest' + }); + + await fastify.register(require('@fastify/formbody')); + + await fastify.register(require('@fastify/multipart'), { + limits: { + fileSize: 5 * 1024 * 1024 // 5MB limit for logo uploads + } + }); + + // Register routes + await fastify.register(require('./routes/auth')); + await fastify.register(require('./routes/dashboard')); + await fastify.register(require('./routes/settings')); + await fastify.register(require('./routes/orders')); + + // Initialize printer with config + const appConfig = config.getAll(); + if (appConfig.printerType && appConfig.printerPath) { + try { + printer.initializePrinter(appConfig); + console.log('Printer initialized successfully'); + } catch (error) { + console.error('Failed to initialize printer:', error.message); + console.log('Printer can be configured later in Settings'); + } + } else { + console.log('Printer not configured - configure in Settings'); + } + + // Start order poller + const poller = new OrderPoller(apiClient, database, printer); + const printWorker = new PrintQueueWorker(database, config, printer); + + // Make poller available globally for restart after settings change + fastify.decorate('orderPoller', poller); + fastify.decorate('printWorker', printWorker); + + // Start server + const port = parseInt(process.env.PORT, 10) || 3000; + const host = process.env.HOST || '0.0.0.0'; + + await fastify.listen({ port, host }); + + const addresses = fastify.server.address(); + console.log('\n================================================='); + console.log('Think Link AI Kitchen Agent is running!'); + console.log(`Access at: http://localhost:${port}`); + console.log(`Or use your computer's IP address from other devices`); + console.log('=================================================\n'); + + // Kick off auto-update check (at boot and on interval) + if (isDev) { + console.log('Dev mode detected: skipping auto-update checks'); + } else { + try { checkAndUpdate(); } catch (_) {} + const envIntervalRaw = process.env.UPDATE_CHECK_INTERVAL_MS; + const envIntervalMs = envIntervalRaw ? parseInt(envIntervalRaw, 10) : NaN; + const updateIntervalMs = (!Number.isNaN(envIntervalMs) && envIntervalMs > 0) ? envIntervalMs : (5 * 60 * 1000); + setInterval(() => { try { checkAndUpdate(); } catch (e) { fastify.log.error(e); } }, updateIntervalMs); + } + + // Start polling after server is up + poller.start(); + // Start print queue worker + printWorker.start(); + + // Handle shutdown gracefully (PM2 reload-friendly) + const gracefulShutdown = async () => { + console.log('\nShutting down gracefully...'); + poller.stop(); + try { printWorker.stop(); } catch (_) {} + try { database.close(); } catch (_) {} + try { await fastify.close(); } catch (_) {} + process.exit(0); + }; + + process.on('SIGINT', gracefulShutdown); + process.on('SIGTERM', gracefulShutdown); + process.on('message', (msg) => { if (msg === 'shutdown') gracefulShutdown(); }); + + } catch (error) { + console.error('Failed to start server:', error); + process.exit(1); + } +} + +// Start the application +main(); + diff --git a/test-startup.js b/test-startup.js new file mode 100644 index 0000000..ea5fe5d --- /dev/null +++ b/test-startup.js @@ -0,0 +1,128 @@ +/** + * Kitchen Agent - Startup Test + * Tests the complete server startup and shutdown + */ + +console.log('Testing Kitchen Agent startup...\n'); + +// Load environment +require('dotenv').config(); + +async function testStartup() { + let server = null; + + try { + console.log('Step 1: Initializing database...'); + const database = require('./database'); + database.init(); + console.log(' ✓ Database ready\n'); + + console.log('Step 2: Loading modules...'); + const config = require('./config'); + const apiClient = require('./api-client'); + const printer = require('./printer'); + console.log(' ✓ All modules loaded\n'); + + console.log('Step 3: Starting Fastify server...'); + const Fastify = require('fastify'); + const path = require('path'); + + const fastify = Fastify({ logger: false }); + + // Register plugins + await fastify.register(require('@fastify/view'), { + engine: { ejs: require('ejs') }, + root: path.join(__dirname, 'views') + }); + + await fastify.register(require('@fastify/static'), { + root: path.join(__dirname, 'public'), + prefix: '/public/' + }); + + await fastify.register(require('@fastify/cookie'), { + secret: process.env.COOKIE_SECRET || 'test-secret' + }); + + await fastify.register(require('@fastify/formbody')); + + await fastify.register(require('@fastify/multipart'), { + limits: { fileSize: 5 * 1024 * 1024 } + }); + + console.log(' ✓ Plugins registered\n'); + + console.log('Step 4: Registering routes...'); + await fastify.register(require('./routes/auth')); + await fastify.register(require('./routes/dashboard')); + await fastify.register(require('./routes/settings')); + await fastify.register(require('./routes/orders')); + console.log(' ✓ All routes registered\n'); + + console.log('Step 5: Starting server on port 3000...'); + await fastify.listen({ port: 3000, host: '127.0.0.1' }); + console.log(' ✓ Server started successfully\n'); + + server = fastify; + + console.log('Step 6: Testing routes...'); + + // Test root route + const response = await fastify.inject({ + method: 'GET', + url: '/' + }); + + if (response.statusCode === 302) { + console.log(' ✓ Root route works (redirects)'); + } else { + console.log(` ✗ Root route returned ${response.statusCode}`); + hasErrors = true; + } + + // Test login page + const loginResponse = await fastify.inject({ + method: 'GET', + url: '/login' + }); + + if (loginResponse.statusCode === 200) { + console.log(' ✓ Login page loads'); + } else { + console.log(` ✗ Login page returned ${loginResponse.statusCode}`); + hasErrors = true; + } + + console.log('\nStep 7: Shutting down server...'); + await fastify.close(); + console.log(' ✓ Server stopped gracefully\n'); + + console.log('='.repeat(60)); + console.log('✅ STARTUP TEST PASSED'); + console.log('='.repeat(60)); + console.log('\nThe Kitchen Agent is ready to run!'); + console.log('Start with: npm start\n'); + + database.close(); + process.exit(0); + + } catch (error) { + console.error('\n❌ STARTUP TEST FAILED'); + console.error('Error:', error.message); + console.error('\nStack trace:'); + console.error(error.stack); + + if (server) { + try { + await server.close(); + } catch (e) { + // ignore + } + } + + process.exit(1); + } +} + +testStartup(); + diff --git a/test-utils.js b/test-utils.js new file mode 100644 index 0000000..2c7c110 --- /dev/null +++ b/test-utils.js @@ -0,0 +1,184 @@ +/** + * Test utilities for Kitchen Agent + * Use these functions to manually test the system + */ + +const database = require('./database'); + +// Initialize database first +database.init(); + +/** + * Insert a mock order for testing UI + */ +function insertMockOrder(orderId = null) { + const id = orderId || Math.floor(Math.random() * 10000); + const now = Math.floor(Date.now() / 1000); + + const mockOrder = { + id: id, + botId: 1, + orderStatus: 'new', + order: { + type: Math.random() > 0.5 ? 'delivery' : 'pickup', + items: [ + { + id: 1, + itemName: 'Cheeseburger', + qty: 2, + price: 12.99, + addons: [{ name: 'Extra Cheese', price: 1.00 }], + exclude: [{ name: 'Onions', price: 0 }] + }, + { + id: 2, + itemName: 'French Fries', + qty: 1, + price: 4.99, + addons: [], + exclude: [] + }, + { + id: 3, + itemName: 'Coca-Cola', + qty: 2, + price: 2.50, + addons: [], + exclude: [] + } + ], + amount: 35.97, + taxRate: 8.5, + taxAmount: 3.06, + deliveryFee: 5.00, + totalAmount: 44.03, + deliveryAddress: '123 Main Street, Anytown, USA 12345', + deliveryInstructions: 'Ring doorbell, leave at door', + specialInstructions: 'Extra napkins please, no ketchup', + foodAllergy: Math.random() > 0.7, + foodAllergyNotes: 'Severe peanut allergy - please ensure no cross-contamination' + }, + customer: { + id: 1, + name: 'John Doe', + phoneNumber: '+15551234567', + email: 'john.doe@example.com' + }, + totalAmount: 44.03, + createdAt: now, + updatedAt: now + }; + + try { + database.insertOrder(mockOrder); + console.log(`✓ Mock order #${id} inserted successfully`); + return mockOrder; + } catch (error) { + console.error('Failed to insert mock order:', error.message); + return null; + } +} + +/** + * Insert multiple mock orders + */ +function insertMultipleMockOrders(count = 5) { + console.log(`Inserting ${count} mock orders...`); + + for (let i = 0; i < count; i++) { + const baseId = 10000 + i; + insertMockOrder(baseId); + } + + console.log(`✓ ${count} mock orders inserted`); +} + +/** + * Clear all orders from database (for testing) + */ +function clearAllOrders() { + try { + database.db.exec('DELETE FROM orders'); + database.db.exec('DELETE FROM print_queue'); + console.log('✓ All orders cleared'); + } catch (error) { + console.error('Failed to clear orders:', error.message); + } +} + +/** + * View current configuration + */ +function viewConfig() { + const config = database.getConfig(); + console.log('\nCurrent Configuration:'); + console.log('====================='); + for (const [key, value] of Object.entries(config)) { + if (key === 'authToken' && value) { + console.log(`${key}: [ENCRYPTED]`); + } else { + console.log(`${key}: ${value}`); + } + } + console.log('=====================\n'); +} + +/** + * View order statistics + */ +function viewStats() { + const stats = database.getOrderStats(); + console.log('\nOrder Statistics:'); + console.log('================='); + console.log(`Total Today: ${stats.total}`); + console.log(`New: ${stats.new}`); + console.log(`Preparing: ${stats.preparing}`); + console.log(`Ready: ${stats.ready}`); + console.log('=================\n'); +} + +/** + * List all orders + */ +function listOrders(limit = 10) { + const orders = database.getOrders({ limit }); + console.log(`\nRecent Orders (${orders.length}):`); + console.log('==================='); + + orders.forEach(order => { + console.log(`#${order.id} | ${order.customer.name} | ${order.localStatus} | $${order.totalAmount.toFixed(2)}`); + }); + + console.log('===================\n'); +} + +// Export functions +module.exports = { + insertMockOrder, + insertMultipleMockOrders, + clearAllOrders, + viewConfig, + viewStats, + listOrders +}; + +// If run directly, show help +if (require.main === module) { + console.log('\nKitchen Agent Test Utilities'); + console.log('============================\n'); + console.log('Usage:'); + console.log(' node test-utils.js\n'); + console.log('Available in Node REPL:'); + console.log(' const test = require("./test-utils");\n'); + console.log(' test.insertMockOrder(); // Insert one mock order'); + console.log(' test.insertMultipleMockOrders(5); // Insert 5 mock orders'); + console.log(' test.listOrders(); // List recent orders'); + console.log(' test.viewStats(); // View order statistics'); + console.log(' test.viewConfig(); // View configuration'); + console.log(' test.clearAllOrders(); // Clear all orders (careful!)\n'); + + // Show current stats + viewStats(); + listOrders(5); +} + diff --git a/updater.js b/updater.js new file mode 100644 index 0000000..b50c3af --- /dev/null +++ b/updater.js @@ -0,0 +1,94 @@ +const path = require('path'); +const os = require('os'); +const fetch = require('node-fetch'); + +const { + GITEA_TOKEN, + REPO_URL = 'https://repo.cloud.thinklink.ai/thinklink/kitchen-agent.git', + REPO_BRANCH = 'main', + PM2_APP = 'kitchen-agent' +} = process.env; + +const repositoryNormalized = REPO_URL.endsWith('.git') ? REPO_URL : `${REPO_URL}.git`; + +const updaterOptions = { + repository: repositoryNormalized, + branch: REPO_BRANCH, + token: GITEA_TOKEN, + // place updates in OS temp dir to avoid permission/self-copy issues + tempLocation: path.join(os.tmpdir(), 'kitchen-agent-updates'), + // only include files that actually exist in the upstream repo; module uses unlinkSync + ignoreFiles: ['.env'], + executeOnComplete: process.platform === 'win32' + ? `IF EXIST package-lock.json (npm ci --omit=dev) ELSE (npm install --omit=dev) & pm2 reload ${PM2_APP}` + : `[ -f package-lock.json ] && npm ci --omit=dev || npm install --omit=dev; pm2 reload ${PM2_APP}`, + exitOnComplete: false +}; + +let updaterInstance = null; + +async function getUpdater() { + if (updaterInstance) { return updaterInstance; } + const mod = await import('auto-git-update'); + const AutoGitUpdate = mod && mod.default ? mod.default : mod; + updaterInstance = new AutoGitUpdate(updaterOptions); + return updaterInstance; +} + +async function checkAndUpdate() { + try { + const updater = await getUpdater(); + + const isGithub = /github\.com/i.test(repositoryNormalized); + let needsUpdate = false; + + if (!isGithub) { + // Use Gitea-aware version check to avoid library's GitHub-only path + const localVersion = (() => { + try { return require('./package.json').version || null; } catch (_) { return null; } + })(); + const base = repositoryNormalized.replace(/\.git$/i, ''); + const rawUrl = `${base}/raw/branch/${REPO_BRANCH}/package.json`; + + async function tryFetch(url) { + const headers = GITEA_TOKEN ? { Authorization: `token ${GITEA_TOKEN}` } : undefined; + const r = await fetch(url, { headers }); + if (r.ok) return r.json(); + return null; + } + + let remotePkg = await tryFetch(rawUrl); + if (!remotePkg && GITEA_TOKEN) { + remotePkg = (await tryFetch(`${rawUrl}?token=${GITEA_TOKEN}`)) + || (await tryFetch(`${rawUrl}?access_token=${GITEA_TOKEN}`)); + } + + const remoteVersion = remotePkg && remotePkg.version ? String(remotePkg.version) : null; + if (!remoteVersion) { + console.error('[auto-update] Unable to read remote version from Gitea raw endpoint'); + } else if (!localVersion || remoteVersion !== String(localVersion)) { + console.log(`[auto-update] Update available: ${localVersion || 'unknown'} -> ${remoteVersion}`); + needsUpdate = true; + } + + if (needsUpdate) { + await updater.forceUpdate(); + } else { + console.log('[auto-update] Up to date'); + } + return; + } + + // GitHub-based repos: use library's compareVersions, then forceUpdate to avoid double compare + const res = await updater.compareVersions(); + if (res && res.upToDate === false) { + await updater.forceUpdate(); + } else { + console.log('[auto-update] Up to date'); + } + } catch (err) { + console.error('[auto-update] failed:', err); + } +} + +module.exports = { checkAndUpdate }; diff --git a/views/dashboard.ejs b/views/dashboard.ejs new file mode 100644 index 0000000..5f85ea7 --- /dev/null +++ b/views/dashboard.ejs @@ -0,0 +1,145 @@ + + + + + + Kitchen Display - Kitchen Agent + + + +
+ +
+ + <% if (showStats) { %> +
+
+
Total Today
+
<%= stats.total %>
+
+
+
New
+
<%= stats.new %>
+
+
+
Preparing
+
<%= stats.preparing %>
+
+
+
Ready
+
<%= stats.ready %>
+
+
+ <% } %> + +
+
+ + + + +
+ +
+ +
+
Loading orders...
+
+ + + + + + + + + + + +
+ + + + + + + diff --git a/views/login.ejs b/views/login.ejs new file mode 100644 index 0000000..206972e --- /dev/null +++ b/views/login.ejs @@ -0,0 +1,80 @@ + + + + + + Login - Kitchen Agent + + + + + + + + + + diff --git a/views/settings.ejs b/views/settings.ejs new file mode 100644 index 0000000..7f910e1 --- /dev/null +++ b/views/settings.ejs @@ -0,0 +1,523 @@ + + + + + + Settings - Kitchen Agent + + + +
+
+

Settings

+ +
+
+ +
+ <% if (message) { %> +
+ <%= message %> +
+ <% } %> + + <% if (error) { %> +
+ <%= error %> +
+ <% } %> + +
+ + + +
+ +
+ + +
+

General Settings

+ +
+ + + Select which bot's orders to display +
+ +
+ + + How often to check for new orders (default: 15000 = 15 seconds) +
+ +
+ + + How often to refresh the dashboard display (default: 10000 = 10 seconds) +
+ +
+ +
+ +

Sound Notifications

+ +
+ + Play sounds when new orders arrive or orders are canceled +
+ +
+ + + Volume: <%= config.soundVolume || 80 %>% +
+ +
+

New Order Sound

+
+ +

+ <%= config.newOrderSoundPath || '/public/sounds/new-order-notification.mp3' %> +

+
+ +
+ + + + + Supported formats: MP3, WAV, OGG +
+
+ +
+

Canceled Order Sound

+
+ +

+ <%= config.canceledOrderSoundPath || '/public/sounds/canceled-order-notification.mp3' %> +

+
+ +
+ + + + + Supported formats: MP3, WAV, OGG +
+
+
+ + +
+

Printer Management

+

Configure individual printers with specific paper sizes, templates, and settings.

+ +
+
+

Configured Printers

+ +
+ +
+ +
Loading printers...
+
+
+
+ + +
+

Receipt Template

+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ + + Cell size (2–8). Larger = bigger QR. +
+ +
+ + +
+ +
+ + + Use placeholders: {id}, {total}, {type}, {createdAt} +
+ +

Show/Hide Sections

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + + Upload a logo image to print at the top of receipts + <% if (config.logoPath) { %> + + <% } %> +
+ +

Business Information

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + Controls the size of address/phone/website/email in header. +
+
+ +
+ +
+
+
+ + + + + + + + +