Compare commits

...

3 Commits

9 changed files with 595 additions and 89 deletions

View File

@ -33,21 +33,28 @@ websocket.initialize(server);
// Static images mapping // Static images mapping
const staticImages = { const staticImages = {
mulberry: 'am_mulberry_logo_wide_color.png' ,
home: 'payment.png', home: 'payment.png',
scan: 'Scanner.png', scan: 'Scanner.png',
printer: 'Check.png', printer: 'Check.png',
settings: 'Settings.png', settings: 'Settings.png',
pinpad: 'pinpad.png', pinpad: 'pinpad.png',
cashier: 'cashier.png', cashier: 'cashier.png',
menu: 'Menu.png',
}; };
// Topics list // Topics list
const topics = [ const topics = [
{ deviceName: 'M50F_Second', deviceID: '01534090202502210057' },
{ deviceName: 'M50F_Igor', deviceID: '01534090202502210142' }, { deviceName: 'M50F_Igor', deviceID: '01534090202502210142' },
{ deviceName: 'M60_Igor', deviceID: '01620013202312220735' }, { deviceName: 'M60_Igor', deviceID: '01620013202312220735' },
{ deviceName: 'M70_Igor', deviceID: '01723060202412010280' }, { deviceName: 'M70_Igor', deviceID: '01723060202412010280' },
{ deviceName: 'M30_Igor', deviceID: '01364100202503060196' }, { deviceName: 'M30_Igor', deviceID: '01364100202503060196' },
{ deviceName: 'M80F_Ahmed', deviceID: '01804042025072100015' },
{ deviceName: 'M80K_Ahmed', deviceID: '78040132025082600001' },
{ deviceName: 'M50F_Ahmed', deviceID: '01534090202502210065' },
{ deviceName: 'M50F_Ahmed', deviceID: '01534090202502210065' }, { deviceName: 'M50F_Ahmed', deviceID: '01534090202502210065' },
{ deviceName: 'M60_Ahmed', deviceID: '01620013202312221500' }, { deviceName: 'M60_Ahmed', deviceID: '01620013202312221500' },
{ deviceName: 'M30_Ahmed', deviceID: '01364100202503060115' }, { deviceName: 'M30_Ahmed', deviceID: '01364100202503060115' },
@ -98,10 +105,17 @@ const defaultConfig = {
type: "base64", type: "base64",
value: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaQAAAWY" value: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaQAAAWY"
}, },
printLogo: {
type: "base64",
value: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaQAAAWY"
},
printURL: "www.mulberrypos.ru",
footerLogoVisibility: "true", footerLogoVisibility: "true",
bankMode: false,
footerGreetingText: "Привет Игорь! ))", footerGreetingText: "Привет Игорь! ))",
footerText: "Mulberry, OOO Demo ©2025", footerText: "Mulberry, OOO Demo ©2025",
primaryColor: "#002233" primaryColor: "#002233",
secondaryColor: "#67777eff",
}; };
// Routes // Routes

View File

@ -4,6 +4,7 @@ body {
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 20px; padding: 20px;
font-size: large;
margin: 0; margin: 0;
} }
@ -14,17 +15,20 @@ body {
background: white; background: white;
padding: 25px; padding: 25px;
/* box-shadow: 0 0 10px rgba(0,0,0,0.1); */ /* box-shadow: 0 0 10px rgba(0,0,0,0.1); */
border-top: 5px solid #00b8d7; /* border-top: 5px solid #00b8d7; */
} }
.header { .header {
text-align: center; text-align: center;
margin-bottom: 20px; margin-bottom: 20px;
} }
.header h1 { .header h1 {
color: #f03024; color: #f03024;
font-size: 24px; font-size: 24px;
margin: 0; margin: 0;
} }
.header p { .header p {
margin: 5px 0; margin: 5px 0;
color: #666; color: #666;
@ -42,19 +46,23 @@ body {
border-top: 1px dashed #ccc; border-top: 1px dashed #ccc;
margin: 15px 0; margin: 15px 0;
} }
.receipt-details { .receipt-details {
margin-bottom: 20px; margin-bottom: 20px;
font-size: smaller; font-size: smaller;
} }
.receipt-row { .receipt-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 8px 0; margin: 8px 0;
} }
.label { .label {
font-weight: bold; font-weight: bold;
color: #333; color: #333;
} }
.footer { .footer {
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
@ -71,7 +79,8 @@ body {
justify-content: center; justify-content: center;
} }
#payButton, #cancelButton { #payButton,
#cancelButton {
padding: 10px 20px; padding: 10px 20px;
border: none; border: none;
border-radius: 5px; border-radius: 5px;
@ -81,21 +90,24 @@ body {
} }
#payButton { #payButton {
background-color: #4CAF50; /* Green */ background-color: #553285;
color: white; color: white;
} }
#payButton:hover { #payButton:hover {
background-color: #45a049; background-color: #cdbbe7;
} }
#cancelButton { #cancelButton {
background-color: #f44336; /* Red */
color: white; color: white;
border-style: solid;
background-color: #f3f3f3;
color: #553285;
} }
#cancelButton:hover { #cancelButton:hover {
background-color: #d32f2f; background-color: #f7bef2;
} }
@ -103,7 +115,8 @@ body {
position: absolute; position: absolute;
top: 150px; top: 150px;
display: none; /* Hide loading circle by default */ display: none;
/* Hide loading circle by default */
border: 4px solid #f3f3f3; border: 4px solid #f3f3f3;
border-top: 4px solid #3498db; border-top: 4px solid #3498db;
border-radius: 50%; border-radius: 50%;
@ -114,6 +127,58 @@ body {
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% {
100% { transform: rotate(360deg); } transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
div#buttons {
width: 300px;
background: white;
padding: 25px;
}
/* .bearHolder{
place-self: center;
justify-self: center;
}
.bear-image{
height: 292px;
} */
.bearHolder {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
/* Optional: set a specific height if needed */
/* height: 300px; */
overflow: hidden; /* Crop anything outside the container */
height: 200px; /* Set your desired height */
}
.bear-image {
max-width: 64%;
max-height: 74%;
height: auto;
width: auto;
display: block;
transform: scale(1.8);
/* Removes extra space below image */
}
/* Fallback for older browsers (optional) */
.bearHolder {
text-align: center; /* Fallback for very old browsers */
}
.bear-image {
margin: 0 auto; /* Fallback for very old browsers */
} }

View File

@ -283,6 +283,27 @@ textarea {
} }
.receipt_container {
text-align: center;
margin-top: 29px;
width: 65%;
padding: 20px;
background: #f5f5f5;
justify-items: center;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
#printLogoPreview {
width: 144px;
}
.printer_container {
display: flex ;
justify-content: space-around;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 MiB

304
public/js/presets.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,7 @@ const imageStore = {
settings: 'Settings.png', settings: 'Settings.png',
pinpad: 'pinpad.png', pinpad: 'pinpad.png',
cashier: 'cashier.png', cashier: 'cashier.png',
menu: 'Menu.png',
mulberry: 'am_mulberry_logo_wide_color.png', mulberry: 'am_mulberry_logo_wide_color.png',
overtec: 'am_overtec_logo_wide_color.png', overtec: 'am_overtec_logo_wide_color.png',
datexpay: 'am_datexpay_logo_wide_color.png', datexpay: 'am_datexpay_logo_wide_color.png',
@ -50,7 +51,7 @@ document.addEventListener('DOMContentLoaded', function () {
console.log('[DEBUG] Initializing form event listeners'); console.log('[DEBUG] Initializing form event listeners');
// Handle logo type toggling // Handle logo type toggling
document.querySelectorAll('[name="mainLogoType"], [name="footerLogoType"]').forEach(select => { document.querySelectorAll('[name="mainLogoType"], [name="footerLogoType"], [name="printLogoType"] ').forEach(select => {
select.addEventListener('change', function () { select.addEventListener('change', function () {
const type = this.value; const type = this.value;
console.log(`[DEBUG] Logo type changed to ${type}`); console.log(`[DEBUG] Logo type changed to ${type}`);
@ -75,6 +76,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Handle image uploads (updated for SVG support) // Handle image uploads (updated for SVG support)
document.querySelectorAll('.upload-input').forEach(input => { document.querySelectorAll('.upload-input').forEach(input => {
input.addEventListener('change', async function () { input.addEventListener('change', async function () {
@ -204,7 +206,7 @@ document.addEventListener('DOMContentLoaded', function () {
} catch (error) { } catch (error) {
console.error('[ERROR] Upload failed:', error); console.error('[ERROR] Upload failed:', error);
alert('Error uploading image. Please try again.');
} }
}); });
}); });
@ -224,11 +226,6 @@ document.addEventListener('DOMContentLoaded', function () {
const config = buildConfigObject(formData); const config = buildConfigObject(formData);
console.log('[DEBUG] Final configuration:', config); console.log('[DEBUG] Final configuration:', config);
// Here you would typically send the config to your server
// fetch('/save-config', { method: 'POST', body: JSON.stringify(config) })
// .then(response => response.json())
// .then(data => console.log('Success:', data))
// .catch(error => console.error('Error:', error));
fetch('/sendToDevice', { fetch('/sendToDevice', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@ -259,10 +256,14 @@ document.addEventListener('DOMContentLoaded', function () {
navItems: [], navItems: [],
mainLogo: getLogoConfig(formData, 'mainLogo'), mainLogo: getLogoConfig(formData, 'mainLogo'),
footerLogo: getLogoConfig(formData, 'footerLogo'), footerLogo: getLogoConfig(formData, 'footerLogo'),
printLogo: getLogoConfig(formData, 'printLogo'),
printURL: formData.get('printURL'),
footerLogoVisibility: formData.get('footerLogoVisibility') === 'on', footerLogoVisibility: formData.get('footerLogoVisibility') === 'on',
bankMode: formData.get('bankMode') === 'on',
footerGreetingText: formData.get('footerGreetingText'), footerGreetingText: formData.get('footerGreetingText'),
footerText: formData.get('footerText'), footerText: formData.get('footerText'),
primaryColor: formData.get('primaryColor') primaryColor: formData.get('primaryColor'),
secondaryColor: formData.get('secondaryColor'),
}; };
// Process navigation items // Process navigation items
@ -289,7 +290,6 @@ document.addEventListener('DOMContentLoaded', function () {
value = imageStore.getBase64(storageKey); value = imageStore.getBase64(storageKey);
} }
// console.log(`[DEBUG] ${prefix} config - type: ${type}, value: ${value ? 'exists' : 'null'}`);
return { return {
type, type,
value, value,
@ -453,6 +453,19 @@ document.addEventListener('DOMContentLoaded', function () {
} }
} }
// Update footer logo preview (similar to main logo)
const printLogoType = formData.get('printLogoType');
if (printLogoType === 'resource') {
const logoValue = formData.get('printLogoResource');
document.getElementById('printLogoPreview').src = imageStore.getStaticImage(logoValue);
} else if (printLogoType === 'base64') {
const storageKey = formData.get('printLogoValue');
const base64Data = imageStore.getBase64(storageKey);
if (base64Data) {
document.getElementById('printLogoPreview').src = base64Data;
}
}
// NEW: Update footer visibility // NEW: Update footer visibility
const footerLogoVisibility = formData.get('footerLogoVisibility') === 'on'; const footerLogoVisibility = formData.get('footerLogoVisibility') === 'on';
const footerLogoElement = document.getElementById('footerLogoPreview'); const footerLogoElement = document.getElementById('footerLogoPreview');
@ -467,6 +480,13 @@ document.addEventListener('DOMContentLoaded', function () {
footerGreetingElement.textContent = footerGreetingText; footerGreetingElement.textContent = footerGreetingText;
} }
// NEW: Update footer greeting text
const printURL = formData.get('printURL') || 'www.mulberrypos.ru';
const printURLElement = document.getElementById('printURLPreview');
if (printURLElement) {
printURLElement.textContent = printURL;
}
// NEW: Update footer text // NEW: Update footer text
const footerText = formData.get('footerText') || 'Mulberry ©2025'; const footerText = formData.get('footerText') || 'Mulberry ©2025';
const footerTextElement = document.getElementById('footerText'); const footerTextElement = document.getElementById('footerText');
@ -476,6 +496,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Update primary color preview // Update primary color preview
const primaryColor = formData.get('primaryColor') || '#002233'; const primaryColor = formData.get('primaryColor') || '#002233';
const secondaryColor = formData.get('secondaryColor') ||'#85969fff' ;
const previewButtons = document.querySelectorAll('.preview-button'); const previewButtons = document.querySelectorAll('.preview-button');
previewButtons.forEach(button => { previewButtons.forEach(button => {
button.style.backgroundColor = primaryColor; button.style.backgroundColor = primaryColor;
@ -511,6 +532,10 @@ document.addEventListener('DOMContentLoaded', function () {
footerLogo: { footerLogo: {
type: config.footerLogo.type, type: config.footerLogo.type,
value: config.footerLogo.value ? '[...base64 data...]' : null value: config.footerLogo.value ? '[...base64 data...]' : null
},
printLogo: {
type: config.printLogo.type,
value: config.printLogo.value ? '[...base64 data...]' : null
} }
}; };
@ -539,9 +564,15 @@ document.addEventListener('DOMContentLoaded', function () {
} }
// Initialize the application // Initialize the application
initializeForm(); initializeForm();
updateConfigLog(); updateConfigLog();
updatePreview(); updatePreview();
}); });

View File

@ -22,14 +22,14 @@ const translations = {
receiptNumber: "Квитанция №", receiptNumber: "Квитанция №",
paymentType: "Тип оплаты:", paymentType: "Тип оплаты:",
testPayment: "Тестовый платеж:", testPayment: "Тестовый платеж:",
paymentAddress: "АДРЕС ПЛАТЕЖА:", paymentAddress: "Адрес:",
issueDate: АТА ВЫПУСКА:", issueDate: ата:",
total: "ИТОГО:", total: "ИТОГО:",
thankYou: "Спасибо за использование Mulberry", thankYou: "Спасибо за использование Mulberry",
transactionId: "ID транзакции:", transactionId: "ID транзакции:",
status: "Статус:", status: "Статус:",
receiptRejected: "Квитанция отклонена", receiptRejected: "Операция отклонена",
paymentDate: АТА ПЛАТЕЖА:", paymentDate: ата платежа:",
amountToPay: "Сумма к оплате:", amountToPay: "Сумма к оплате:",
cancel: "Отмена", cancel: "Отмена",
pay: "Оплатить", pay: "Оплатить",

View File

@ -19,9 +19,26 @@
<div class="container"> <div class="container">
<!-- Preview Section --> <!-- Preview Section -->
<div class="preview"> <div class="preview">
<div class="form-group">
<label>Configuration Presets</label>
<select id="presetSelector">
<option value="">-- Select a preset --</option>
<option value="default">Default Configuration</option>
<option value="bank">Bank Mode</option>
<option value="minimal">Minimal Interface</option>
<option value="D30_bankmode_false">D30 - BankMode OFF</option>
<option value="D30_bankmode_true">D30 - BankMode ON</option>
<option value="minimal">Minimal Interface</option>
<option value="minimal">Minimal Interface</option>
</select>
<button type="button" id="sendPresetBtn" style="margin-top: 10px;">Send Selected Preset</button>
</div>
<div class="pos-terminal"> <div class="pos-terminal">
<!-- Main Logo --> <!-- Main Logo -->
<div class="main-logo-preview"> <div class="main-logo-preview">
@ -47,7 +64,25 @@
<div id="footerText">Mulberry ©2025</div> <div id="footerText">Mulberry ©2025</div>
</div> </div>
</div> </div>
<div class="receipt_container">
<p>Receipt #00123</p>
<% if (defaultConfig.printLogo) { %>
<img id="printLogoPreview" src="/images/drawable/am_mulberry_logo_wide_color.png">
<% } %>
<p>Total ............ 10.00$</p>
<% if (defaultConfig.printURL) { %>
<p id="printURLPreview"></p>
<% } %>
</div> </div>
</div>
<!-- Configuration Form --> <!-- Configuration Form -->
<div class="config-form"> <div class="config-form">
@ -105,11 +140,11 @@
<option value="NAV_SETTINGS" <%=item.navPage==='NAV_SETTINGS' ? 'selected' : '' <option value="NAV_SETTINGS" <%=item.navPage==='NAV_SETTINGS' ? 'selected' : ''
%>>Settings</option> %>>Settings</option>
<option value="NAV_PINPAD" <%=item.navPage==='NAV_PINPAD' ? 'selected' : '' <option value="NAV_PINPAD" <%=item.navPage==='NAV_PINPAD' ? 'selected' : '' %>
%>>PinPad</option> >PinPad</option>
<option value="NAV_CASHIER" <%=item.navPage==='NAV_CASHIER' ? 'selected' : '' <option value="NAV_CASHIER" <%=item.navPage==='NAV_CASHIER' ? 'selected' : '' %>
%>>Cashier</option> >Cashier</option>
</select> </select>
@ -187,6 +222,41 @@
</label> </label>
</div> </div>
<div class="form-group">
<label>Bank Mode</label>
<label class="switch">
<input type="checkbox" name="bankMode" <%=defaultConfig.bankMode ? 'checked' : '' %>>
<span class="slider round"></span>
</label>
</div>
<div class="printer_container">
<!-- Print Config -->
<div class="form-group">
<label>Print Logo</label>
<select name="printLogoType">
<option value="resource">Static Image</option>
<option value="base64">Upload Image</option>
</select>
<select name="printLogoResource" class="resource-select">
<% staticImages.forEach(img=> { %>
<option value="<%= img %>">
<%= img %>
</option>
<% }); %>
</select>
<input type="file" name="printLogoUpload" class="upload-input" style="display:none;">
</div>
<div class="form-group">
<label>Print URL</label>
<input type="text" name="printURL" value="www.mulberrypos.ru">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label>Greeting Text</label> <label>Greeting Text</label>
<input type="text" name="footerGreetingText" value="Hello!"> <input type="text" name="footerGreetingText" value="Hello!">
@ -194,23 +264,17 @@
<div class="form-group"> <div class="form-group">
<label>Footer Text</label> <label>Footer Text</label>
<input type="text" name="footerText" value="Mulberry ©2025"> <input type="text" name="footerText" value="©Mulberry 2025">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Primary Color</label> <label>Primary Color</label>
<input type="color" name="primaryColor" value="#002233"> <input type="color" name="primaryColor" value="#bb2aa1">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Primary Color</label> <label>Secondary Color</label>
<input type="color" name="primaryColor" value="#002233"> <input type="color" name="secondaryColor" value="#4aa6d3">
<!-- Add this preview button -->
<div class="color-preview" style="margin-top: 10px;">
<button class="preview-button" style="padding: 8px 16px; border-radius: 4px;">
Submit Button Preview
</button>
</div>
</div> </div>
</form> </form>
@ -218,13 +282,15 @@
<!-- Log Area --> <!-- Log Area -->
<div class="form-group"> <div class="form-group">
<label>Configuration JSON</label> <label>Configuration JSON</label>
<textarea id="configLog" readonly rows="10" style="width: 100%;"></textarea> <textarea id="configLog" readonly rows="20" style="width: 100%;"></textarea>
</div> </div>
</div> </div>
</div> </div>
<script src="/js/script.js"></script> <script src="/js/script.js"></script>
<script src="/js/presets.js"></script>
</body> </body>
</html> </html>

View File

@ -8,11 +8,11 @@
</head> </head>
<body> <body>
<!-- Accepted Receipt --> <!-- Accepted Receipt -->
<div id="acceptedReceipt" class="receipt" style="border-top: 5px solid <%= appColor %>;"> <div id="acceptedReceipt" class="receipt" >
<div class="header"> <div class="header">
<!-- <img class="datex-pay-logo" src="/images/dynamic/<%= appLogo %>.png" alt="" srcset=""> --> <!-- <img class="datex-pay-logo" src="/images/dynamic/<%= appLogo %>.png" alt="" srcset=""> -->
<img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset=""> <img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset="">
<p><%= t.receiptNumber %> 2949</p> <!-- <p><%= t.receiptNumber %> 2949</p> -->
<!-- <p>https://www.mulberrypos.com</p> --> <!-- <p>https://www.mulberrypos.com</p> -->
<!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> --> <!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> -->
</div> </div>
@ -41,7 +41,7 @@
<div class="divider"></div> <div class="divider"></div>
<div class="receipt-details"> <div class="receipt-details">
<div style="font-size: large;" class="receipt-row"> <div style="font-size: x-large;" class="receipt-row">
<span class="label"><%= t.total %></span> <span class="label"><%= t.total %></span>
<span><%= amount %> <%= currencyS %></span> <span><%= amount %> <%= currencyS %></span>
</div> </div>
@ -49,6 +49,11 @@
<div class="divider"></div> <div class="divider"></div>
<div class="bearHolder">
<img class="bear-image" src="images/Mishka_like_0109.gif" alt="">
</div>
<div class="divider"></div>
<div class="footer"> <div class="footer">
<p><%= t.thankYou %></p> <p><%= t.thankYou %></p>
<p><%= t.transactionId %> <%= transactionId %></p> <p><%= t.transactionId %> <%= transactionId %></p>
@ -56,10 +61,10 @@
</div> </div>
<!-- Rejected Receipt --> <!-- Rejected Receipt -->
<div id="rejectedReceipt" class="receipt" style="border-top: 5px solid <%= appColor %>;"> <div id="rejectedReceipt" class="receipt" >
<div class="header"> <div class="header">
<img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset=""> <img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset="">
<p><%= t.receiptNumber %> 2949</p> <!-- <p><%= t.receiptNumber %> 2949</p> -->
<!-- <p>https://www.mulberrypos.com</p> --> <!-- <p>https://www.mulberrypos.com</p> -->
<!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> --> <!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> -->
</div> </div>
@ -85,10 +90,10 @@
</div> </div>
<!-- Buttons Section --> <!-- Buttons Section -->
<div id="buttons" class="buttons-section" style="border-top: 5px solid <%= appColor %>;"> <div id="buttons" class="buttons-section" >
<div class="header"> <div class="header">
<img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset=""> <img class="datex-pay-logo" src="/images/am_mulberry_logo_wide_color.png" alt="" srcset="">
<p><%= t.receiptNumber %> 2949</p> <!-- <p><%= t.receiptNumber %> 2949</p> -->
<!-- <p>https://www.mulberrypos.com</p> --> <!-- <p>https://www.mulberrypos.com</p> -->
<!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> --> <!-- <img class="mulberry-logo" src="/images/dynamic/overtec.png" alt="" srcset=""> -->
</div> </div>