discord theme, top bar fix, organized folders

This commit is contained in:
Shiro-Nek0
2026-02-26 02:55:20 -03:00
parent 3f47b3cda4
commit 8cc5138888
5 changed files with 205 additions and 121 deletions

View File

@@ -2,8 +2,8 @@ import os
from PIL import Image from PIL import Image
# --- CONFIGURATION --- # --- CONFIGURATION ---
CARD_DIR = 'keychain_cards' CARD_DIR = os.path.join(os.getcwd(),'keychain_cards')
OUTPUT_PDF = 'keychain_3x3_perfect.pdf' OUTPUT_PDF = os.path.join(os.getcwd(), 'keychain_3x3_perfect.pdf')
# A4 at 300 DPI # A4 at 300 DPI
PAGE_W, PAGE_H = 2480, 3508 PAGE_W, PAGE_H = 2480, 3508

View File

@@ -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. 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.

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="es"> <html lang="es">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <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> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<style> <style>
:root { :root {
--bg: #f4f7f6; /* Discord Light Mode Palette */
--bg: #ebedef;
--card-bg: #ffffff; --card-bg: #ffffff;
--text-main: #333333; --text-main: #2e3338;
--text-muted: #7f8c8d; --text-muted: #4f5660;
--border: #dddddd; --border: #e3e5e8;
--header-bg: #ffffff; --header-bg: #ffffff;
--input-bg: #ffffff; --input-bg: #e3e5e8;
--table-head: #fafafa; --table-head: #f2f3f5;
--price-color: #2c3e50; --price-color: #2e3338;
--accent: #5865F2; /* Discord Burple */
--accent-hover: #4752c4;
--danger: #ed4245;
--warning: #fee75c;
} }
[data-theme="dark"] { [data-theme="dark"] {
--bg: #121212; /* Discord Dark Mode Palette */
--card-bg: #1e1e1e; --bg: #36393f;
--text-main: #e0e0e0; --card-bg: #2f3136;
--text-muted: #a0a0a0; --text-main: #ffffff;
--border: #333333; --text-muted: #b9bbbe;
--header-bg: #1e1e1e; --border: #202225;
--input-bg: #2d2d2d; --header-bg: #202225;
--table-head: #252525; --input-bg: #202225;
--price-color: #ecf0f1; --table-head: #292b2f;
--price-color: #ffffff;
} }
body { body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'gg sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 20px; padding: 20px;
background: var(--bg); background: var(--bg);
color: var(--text-main); color: var(--text-main);
margin: 0; margin: 0;
transition: background 0.3s, color 0.3s; transition: background 0.2s, color 0.2s;
} }
.header-bar { .header-bar {
@@ -47,68 +54,123 @@
align-items: center; align-items: center;
background: var(--header-bg); background: var(--header-bg);
padding: 10px 25px; padding: 10px 25px;
border-radius: 10px; border-radius: 8px;
margin-bottom: 20px; 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; } .main-container {
.column { flex: 1; min-width: 400px; } display: flex;
gap: 25px;
}
.column {
flex: 1;
min-width: 400px;
}
.card { .card {
background: var(--card-bg); background: var(--card-bg);
padding: 25px; padding: 25px;
border-radius: 15px; border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.05); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center; text-align: center;
margin-bottom: 20px; margin-bottom: 20px;
border: 1px solid var(--border);
} }
#display-img { #display-img {
max-width: 250px; max-width: 250px;
max-height: 250px; max-height: 250px;
border-radius: 10px; border-radius: 4px;
margin-bottom: 15px; margin-bottom: 15px;
object-fit: contain; 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 { input {
display: block; display: block;
width: 100%; width: 100%;
margin: 12px 0; margin: 12px 0;
padding: 12px; padding: 12px;
border: 1px solid var(--border); border: none;
border-radius: 8px; border-radius: 4px;
box-sizing: border-box; box-sizing: border-box;
background: var(--input-bg); background: var(--input-bg);
color: var(--text-main); color: var(--text-main);
} }
button { padding: 10px 15px; cursor: pointer; border-radius: 6px; border: none; transition: 0.2s; } button {
.btn-save { background: #2c3e50; color: white; width: 100%; font-size: 1.1em; } padding: 10px 15px;
.btn-save:hover { background: #34495e; } cursor: pointer;
.btn-edit { background: #f1c40f; color: #000; } border-radius: 4px;
.btn-del { background: #e74c3c; color: white; } border: none;
transition: 0.2s;
font-weight: 500;
}
table { width: 100%; border-collapse: collapse; background: var(--card-bg); border-radius: 10px; overflow: hidden; } .btn-save {
th, td { padding: 15px; border-bottom: 1px solid var(--border); text-align: left; } background: var(--accent);
th { background: var(--table-head); } 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 { .theme-toggle-btn {
background: var(--text-main); background: var(--text-main);
color: var(--bg); color: var(--bg);
font-weight: bold; font-weight: bold;
margin-right: 15px;
} }
#new-product-prompt { #new-product-prompt {
display: none; display: none;
background: #3498db; background: var(--accent);
color: white; color: white;
padding: 12px 20px; padding: 12px 20px;
border-radius: 10px; border-radius: 8px;
margin-bottom: 20px; margin-bottom: 20px;
animation: slideDown 0.4s ease; animation: slideDown 0.4s ease;
justify-content: space-between; justify-content: space-between;
@@ -121,14 +183,17 @@
} }
</style> </style>
</head> </head>
<body> <body>
<div class="header-bar"> <div class="header-bar">
<h2 style="margin:0;">SekiPOS v1.1</h2> <h2 style="margin:0;">SekiPOS v1.2</h2>
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center; gap: 15px;">
<button onclick="toggleTheme()" class="theme-toggle-btn" id="theme-text">Modo Oscuro</button> <button onclick="toggleTheme()" class="theme-toggle-btn" id="theme-text" style="margin: 0;">Modo Oscuro</button>
<span>Usuario: <b>{{ user.username }}</b></span> | <span style="color: var(--border);">|</span>
<a href="/logout" style="color: #e74c3c; text-decoration: none; font-weight: bold; margin-left: 10px;">Cerrar Sesión</a> <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>
</div> </div>
@@ -136,7 +201,7 @@
<div class="column"> <div class="column">
<div id="new-product-prompt"> <div id="new-product-prompt">
<span style="flex-grow: 1;">¡Nuevo! Barcode <b><span id="new-barcode-display"></span></b>. ¿Deseas agregarlo?</span> <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>
<div class="card" id="scan-display"> <div class="card" id="scan-display">
@@ -210,12 +275,10 @@
} }
formatTablePrices(); formatTablePrices();
// Dark Mode Logic
function toggleTheme() { function toggleTheme() {
const html = document.documentElement; const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme'); const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme); html.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme); localStorage.setItem('theme', newTheme);
updateThemeUI(newTheme); updateThemeUI(newTheme);
@@ -226,12 +289,11 @@
btnText.innerText = theme === 'dark' ? 'Modo Claro' : 'Modo Oscuro'; btnText.innerText = theme === 'dark' ? 'Modo Claro' : 'Modo Oscuro';
} }
// Initialize theme from storage
const savedTheme = localStorage.getItem('theme') || 'light'; const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme); document.documentElement.setAttribute('data-theme', savedTheme);
updateThemeUI(savedTheme); updateThemeUI(savedTheme);
socket.on('new_scan', function(data) { socket.on('new_scan', function (data) {
dismissPrompt(); dismissPrompt();
document.getElementById('display-name').innerText = data.name; document.getElementById('display-name').innerText = data.name;
document.getElementById('display-price').innerText = clpFormatter.format(data.price); document.getElementById('display-price').innerText = clpFormatter.format(data.price);
@@ -239,22 +301,10 @@
document.getElementById('display-img').src = data.image || './static/placeholder.png'; 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'); const prompt = document.getElementById('new-product-prompt');
document.getElementById('new-barcode-display').innerText = data.barcode; document.getElementById('new-barcode-display').innerText = data.barcode;
document.getElementById('display-price').innerText = "$ ???";
prompt.style.display = 'flex'; 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-barcode').value = data.barcode;
document.getElementById('form-name').value = data.name || ''; document.getElementById('form-name').value = data.name || '';
document.getElementById('form-image').value = data.image || ''; document.getElementById('form-image').value = data.image || '';
@@ -274,23 +324,19 @@
document.getElementById('form-price').value = price; document.getElementById('form-price').value = price;
document.getElementById('form-image').value = image; document.getElementById('form-image').value = image;
document.getElementById('form-title').innerText = "Editando: " + name; document.getElementById('form-title').innerText = "Editando: " + name;
window.scrollTo({top: 0, behavior: 'smooth'}); window.scrollTo({ top: 0, behavior: 'smooth' });
} }
function searchTable() { function searchTable() {
var input = document.getElementById("searchInput"); var input = document.getElementById("searchInput");
var filter = input.value.toUpperCase(); var filter = input.value.toUpperCase();
var tr = document.getElementById("inventoryTable").getElementsByTagName("tr"); var tr = document.getElementById("inventoryTable").getElementsByTagName("tr");
for (var i = 1; i < tr.length; i++) { for (var i = 1; i < tr.length; i++) {
var content = tr[i].textContent || tr[i].innerText; var content = tr[i].textContent || tr[i].innerText;
tr[i].style.display = content.toUpperCase().indexOf(filter) > -1 ? "" : "none"; tr[i].style.display = content.toUpperCase().indexOf(filter) > -1 ? "" : "none";
} }
} }
socket.on('product_deleted', function(data) {
window.location.reload();
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -5,87 +5,125 @@
<title>SekiPOS Login</title> <title>SekiPOS Login</title>
<style> <style>
:root { :root {
--bg: #2c3e50; /* Discord Branding Colors */
--bg: #5865F2;
--card-bg: #ffffff; --card-bg: #ffffff;
--text: #333333; --text: #2e3338;
--input-bg: #ffffff; --input-bg: #e3e5e8;
--input-border: #cccccc; --input-border: transparent;
--accent: #5865F2;
--accent-hover: #4752c4;
--error: #ed4245;
} }
[data-theme="dark"] { [data-theme="dark"] {
--bg: #121212; /* Discord Dark Mode */
--card-bg: #1e1e1e; --bg: #36393f;
--text: #e0e0e0; --card-bg: #2f3136;
--input-bg: #2d2d2d; --text: #ffffff;
--input-border: #444444; --input-bg: #202225;
--input-border: transparent;
} }
body { body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'gg sans', 'Segoe UI', Tahoma, sans-serif;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; height: 100vh;
background: var(--bg); background: var(--bg);
margin: 0; margin: 0;
transition: background 0.3s; transition: background 0.2s;
} }
.login-box { .login-box {
background: var(--card-bg); background: var(--card-bg);
padding: 40px; padding: 32px;
border-radius: 12px; border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.3); box-shadow: 0 8px 16px rgba(0,0,0,0.2);
text-align: center; text-align: center;
color: var(--text); 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 { input {
display: block; display: block;
width: 250px; width: 100%;
margin: 10px auto; margin: 16px 0;
padding: 12px; padding: 12px;
border: 1px solid var(--input-border); border: none;
border-radius: 6px; border-radius: 4px;
background: var(--input-bg); background: var(--input-bg);
color: var(--text); color: var(--text);
box-sizing: border-box;
font-size: 16px;
}
input:focus {
outline: 2px solid var(--accent);
} }
button { button {
background: #3498db; background: var(--accent);
color: white; color: white;
border: none; border: none;
padding: 12px 24px; padding: 12px 24px;
border-radius: 6px; border-radius: 4px;
cursor: pointer; cursor: pointer;
font-size: 1.1em; font-size: 16px;
font-weight: 600;
width: 100%; width: 100%;
margin-top: 10px; 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> </style>
</head> </head>
<body> <body>
<div class="login-box"> <div class="login-box">
<h2>SekiPOS Access</h2> <h2>SekiPOS</h2>
<p class="sub-text">¡Hola de nuevo!</p>
{% with messages = get_flashed_messages() %} {% 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 %} {% endwith %}
<form method="POST"> <form method="POST">
<input type="text" name="username" placeholder="Username" required> <input type="text" name="username" placeholder="Usuario" required>
<input type="password" name="password" placeholder="Password" required> <input type="password" name="password" placeholder="Contraseña" required>
<button type="submit">Unlock</button> <button type="submit">Iniciar Sesión</button>
</form> </form>
</div> </div>
<script> <script>
// Sincronizar tema con el index // Sync theme with main page
const savedTheme = localStorage.getItem('theme') || 'light'; const savedTheme = localStorage.getItem('theme') || 'light';
if (savedTheme === 'dark') { if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark'); document.documentElement.setAttribute('data-theme', 'dark');