discord theme, top bar fix, organized folders
This commit is contained in:
@@ -2,8 +2,8 @@ import os
|
||||
from PIL import Image
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
CARD_DIR = 'keychain_cards'
|
||||
OUTPUT_PDF = 'keychain_3x3_perfect.pdf'
|
||||
CARD_DIR = os.path.join(os.getcwd(),'keychain_cards')
|
||||
OUTPUT_PDF = os.path.join(os.getcwd(), 'keychain_3x3_perfect.pdf')
|
||||
|
||||
# A4 at 300 DPI
|
||||
PAGE_W, PAGE_H = 2480, 3508
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SekiPOS v1.1 🍫🥤
|
||||
# SekiPOS v1.2 🍫🥤
|
||||
|
||||
A reactive POS inventory system for software engineers with a snack addiction. Features real-time UI updates, automatic product discovery via Open Food Facts, and local image caching.
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -7,38 +8,44 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4f7f6;
|
||||
/* Discord Light Mode Palette */
|
||||
--bg: #ebedef;
|
||||
--card-bg: #ffffff;
|
||||
--text-main: #333333;
|
||||
--text-muted: #7f8c8d;
|
||||
--border: #dddddd;
|
||||
--text-main: #2e3338;
|
||||
--text-muted: #4f5660;
|
||||
--border: #e3e5e8;
|
||||
--header-bg: #ffffff;
|
||||
--input-bg: #ffffff;
|
||||
--table-head: #fafafa;
|
||||
--price-color: #2c3e50;
|
||||
--input-bg: #e3e5e8;
|
||||
--table-head: #f2f3f5;
|
||||
--price-color: #2e3338;
|
||||
--accent: #5865F2; /* Discord Burple */
|
||||
--accent-hover: #4752c4;
|
||||
--danger: #ed4245;
|
||||
--warning: #fee75c;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg: #121212;
|
||||
--card-bg: #1e1e1e;
|
||||
--text-main: #e0e0e0;
|
||||
--text-muted: #a0a0a0;
|
||||
--border: #333333;
|
||||
--header-bg: #1e1e1e;
|
||||
--input-bg: #2d2d2d;
|
||||
--table-head: #252525;
|
||||
--price-color: #ecf0f1;
|
||||
/* Discord Dark Mode Palette */
|
||||
--bg: #36393f;
|
||||
--card-bg: #2f3136;
|
||||
--text-main: #ffffff;
|
||||
--text-muted: #b9bbbe;
|
||||
--border: #202225;
|
||||
--header-bg: #202225;
|
||||
--input-bg: #202225;
|
||||
--table-head: #292b2f;
|
||||
--price-color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-family: 'gg sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
background: var(--bg);
|
||||
color: var(--text-main);
|
||||
margin: 0;
|
||||
transition: background 0.3s, color 0.3s;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
@@ -47,68 +54,123 @@
|
||||
align-items: center;
|
||||
background: var(--header-bg);
|
||||
padding: 10px 25px;
|
||||
border-radius: 10px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.main-container { display: flex; gap: 25px; }
|
||||
.column { flex: 1; min-width: 400px; }
|
||||
.main-container {
|
||||
display: flex;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex: 1;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#display-img {
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
border-radius: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 15px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.price-tag { font-size: 3em; color: var(--price-color); font-weight: 800; margin: 10px 0; }
|
||||
.price-tag {
|
||||
font-size: 3em;
|
||||
color: var(--price-color);
|
||||
font-weight: 800;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
background: var(--input-bg);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
button { padding: 10px 15px; cursor: pointer; border-radius: 6px; border: none; transition: 0.2s; }
|
||||
.btn-save { background: #2c3e50; color: white; width: 100%; font-size: 1.1em; }
|
||||
.btn-save:hover { background: #34495e; }
|
||||
.btn-edit { background: #f1c40f; color: #000; }
|
||||
.btn-del { background: #e74c3c; color: white; }
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
transition: 0.2s;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
table { width: 100%; border-collapse: collapse; background: var(--card-bg); border-radius: 10px; overflow: hidden; }
|
||||
th, td { padding: 15px; border-bottom: 1px solid var(--border); text-align: left; }
|
||||
th { background: var(--table-head); }
|
||||
.btn-save {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
width: 100%;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.btn-save:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-del {
|
||||
background: var(--danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--table-head);
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8em;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.theme-toggle-btn {
|
||||
background: var(--text-main);
|
||||
color: var(--bg);
|
||||
font-weight: bold;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
#new-product-prompt {
|
||||
display: none;
|
||||
background: #3498db;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
border-radius: 10px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
animation: slideDown 0.4s ease;
|
||||
justify-content: space-between;
|
||||
@@ -121,14 +183,17 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header-bar">
|
||||
<h2 style="margin:0;">SekiPOS v1.1</h2>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<button onclick="toggleTheme()" class="theme-toggle-btn" id="theme-text">Modo Oscuro</button>
|
||||
<span>Usuario: <b>{{ user.username }}</b></span> |
|
||||
<a href="/logout" style="color: #e74c3c; text-decoration: none; font-weight: bold; margin-left: 10px;">Cerrar Sesión</a>
|
||||
<h2 style="margin:0;">SekiPOS v1.2</h2>
|
||||
<div style="display: flex; align-items: center; gap: 15px;">
|
||||
<button onclick="toggleTheme()" class="theme-toggle-btn" id="theme-text" style="margin: 0;">Modo Oscuro</button>
|
||||
<span style="color: var(--border);">|</span>
|
||||
<span>Usuario: <b>{{ user.username }}</b></span>
|
||||
<span style="color: var(--border);">|</span>
|
||||
<a href="/logout" style="color: var(--danger); text-decoration: none; font-weight: bold;">Cerrar Sesión</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -136,7 +201,7 @@
|
||||
<div class="column">
|
||||
<div id="new-product-prompt">
|
||||
<span style="flex-grow: 1;">¡Nuevo! Barcode <b><span id="new-barcode-display"></span></b>. ¿Deseas agregarlo?</span>
|
||||
<button onclick="dismissPrompt()" style="background:rgba(255,255,255,0.2); color:white; margin-left: 15px;">Omitir</button>
|
||||
<button onclick="dismissPrompt()" style="background:rgba(0,0,0,0.2); color:white; margin-left: 15px;">Omitir</button>
|
||||
</div>
|
||||
|
||||
<div class="card" id="scan-display">
|
||||
@@ -210,12 +275,10 @@
|
||||
}
|
||||
formatTablePrices();
|
||||
|
||||
// Dark Mode Logic
|
||||
function toggleTheme() {
|
||||
const html = document.documentElement;
|
||||
const currentTheme = html.getAttribute('data-theme');
|
||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||
|
||||
html.setAttribute('data-theme', newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
updateThemeUI(newTheme);
|
||||
@@ -226,12 +289,11 @@
|
||||
btnText.innerText = theme === 'dark' ? 'Modo Claro' : 'Modo Oscuro';
|
||||
}
|
||||
|
||||
// Initialize theme from storage
|
||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
||||
updateThemeUI(savedTheme);
|
||||
|
||||
socket.on('new_scan', function(data) {
|
||||
socket.on('new_scan', function (data) {
|
||||
dismissPrompt();
|
||||
document.getElementById('display-name').innerText = data.name;
|
||||
document.getElementById('display-price').innerText = clpFormatter.format(data.price);
|
||||
@@ -239,22 +301,10 @@
|
||||
document.getElementById('display-img').src = data.image || './static/placeholder.png';
|
||||
});
|
||||
|
||||
socket.on('scan_error', function(data) {
|
||||
socket.on('scan_error', function (data) {
|
||||
const prompt = document.getElementById('new-product-prompt');
|
||||
document.getElementById('new-barcode-display').innerText = data.barcode;
|
||||
document.getElementById('display-price').innerText = "$ ???";
|
||||
prompt.style.display = 'flex';
|
||||
|
||||
if (data.name) {
|
||||
document.getElementById('display-name').innerText = data.name + " (Nuevo)";
|
||||
document.getElementById('display-price').innerText = "$ ???";
|
||||
document.getElementById('display-img').src = data.image || './static/placeholder.png';
|
||||
} else {
|
||||
document.getElementById('display-name').innerText = "Producto Desconocido";
|
||||
document.getElementById('display-img').src = './static/placeholder.png';
|
||||
}
|
||||
document.getElementById('display-barcode').innerText = data.barcode;
|
||||
|
||||
document.getElementById('form-barcode').value = data.barcode;
|
||||
document.getElementById('form-name').value = data.name || '';
|
||||
document.getElementById('form-image').value = data.image || '';
|
||||
@@ -274,23 +324,19 @@
|
||||
document.getElementById('form-price').value = price;
|
||||
document.getElementById('form-image').value = image;
|
||||
document.getElementById('form-title').innerText = "Editando: " + name;
|
||||
window.scrollTo({top: 0, behavior: 'smooth'});
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
function searchTable() {
|
||||
var input = document.getElementById("searchInput");
|
||||
var filter = input.value.toUpperCase();
|
||||
var tr = document.getElementById("inventoryTable").getElementsByTagName("tr");
|
||||
|
||||
for (var i = 1; i < tr.length; i++) {
|
||||
var content = tr[i].textContent || tr[i].innerText;
|
||||
tr[i].style.display = content.toUpperCase().indexOf(filter) > -1 ? "" : "none";
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('product_deleted', function(data) {
|
||||
window.location.reload();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -5,87 +5,125 @@
|
||||
<title>SekiPOS Login</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #2c3e50;
|
||||
/* Discord Branding Colors */
|
||||
--bg: #5865F2;
|
||||
--card-bg: #ffffff;
|
||||
--text: #333333;
|
||||
--input-bg: #ffffff;
|
||||
--input-border: #cccccc;
|
||||
--text: #2e3338;
|
||||
--input-bg: #e3e5e8;
|
||||
--input-border: transparent;
|
||||
--accent: #5865F2;
|
||||
--accent-hover: #4752c4;
|
||||
--error: #ed4245;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg: #121212;
|
||||
--card-bg: #1e1e1e;
|
||||
--text: #e0e0e0;
|
||||
--input-bg: #2d2d2d;
|
||||
--input-border: #444444;
|
||||
/* Discord Dark Mode */
|
||||
--bg: #36393f;
|
||||
--card-bg: #2f3136;
|
||||
--text: #ffffff;
|
||||
--input-bg: #202225;
|
||||
--input-border: transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-family: 'gg sans', 'Segoe UI', Tahoma, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background: var(--bg);
|
||||
margin: 0;
|
||||
transition: background 0.3s;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: var(--card-bg);
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
|
||||
text-align: center;
|
||||
color: var(--text);
|
||||
transition: background 0.3s, color 0.3s;
|
||||
width: 400px;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
h2 { margin-top: 0; }
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p.sub-text {
|
||||
color: var(--text);
|
||||
opacity: 0.7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
width: 250px;
|
||||
margin: 10px auto;
|
||||
width: 100%;
|
||||
margin: 16px 0;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 6px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: var(--input-bg);
|
||||
color: var(--text);
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
}
|
||||
|
||||
button {
|
||||
background: #3498db;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
button:hover { background: #2980b9; }
|
||||
button:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.error-msg { color: #e74c3c; font-size: 0.9em; margin-bottom: 10px; }
|
||||
.error-msg {
|
||||
background: var(--error);
|
||||
color: white;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-box">
|
||||
<h2>SekiPOS Access</h2>
|
||||
<h2>SekiPOS</h2>
|
||||
<p class="sub-text">¡Hola de nuevo!</p>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}<p class="error-msg">{{ messages[0] }}</p>{% endif %}
|
||||
{% if messages %}
|
||||
<div class="error-msg">{{ messages[0] }}</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="POST">
|
||||
<input type="text" name="username" placeholder="Username" required>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<button type="submit">Unlock</button>
|
||||
<input type="text" name="username" placeholder="Usuario" required>
|
||||
<input type="password" name="password" placeholder="Contraseña" required>
|
||||
<button type="submit">Iniciar Sesión</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Sincronizar tema con el index
|
||||
// Sync theme with main page
|
||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
||||
if (savedTheme === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
|
||||
Reference in New Issue
Block a user