initial modularization + templates
This commit is contained in:
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Build SekiPOS (F5)",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build-sekipos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
15
.vscode/tasks.json
vendored
Normal file
15
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build-sekipos",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "docker build -t sekipos:latest .",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "always",
|
||||||
|
"panel": "new"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
static/cookieStuff.js
Normal file
17
static/cookieStuff.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
function setCookie(name, value, days = 365) {
|
||||||
|
const d = new Date();
|
||||||
|
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
let expires = "expires=" + d.toUTCString();
|
||||||
|
document.cookie = name + "=" + value + ";" + expires + ";path=/;SameSite=Lax";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
let nameEQ = name + "=";
|
||||||
|
let ca = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < ca.length; i++) {
|
||||||
|
let c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||||
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
255
static/style.css
Normal file
255
static/style.css
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
:root {
|
||||||
|
--bg: #ebedef;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--text-main: #2e3338;
|
||||||
|
--text-muted: #4f5660;
|
||||||
|
--border: #e3e5e8;
|
||||||
|
--navbar-bg: #ffffff;
|
||||||
|
--input-bg: #e3e5e8;
|
||||||
|
--table-head: #f2f3f5;
|
||||||
|
--accent: #5865f2;
|
||||||
|
--accent-hover: #4752c4;
|
||||||
|
--danger: #ed4245;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg: #36393f;
|
||||||
|
--card-bg: #2f3136;
|
||||||
|
--text-main: #dcddde;
|
||||||
|
--text-muted: #b9bbbe;
|
||||||
|
--border: #202225;
|
||||||
|
--navbar-bg: #202225;
|
||||||
|
--input-bg: #202225;
|
||||||
|
--table-head: #292b2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: "gg sans", "Segoe UI", sans-serif;
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Navbar ── */
|
||||||
|
.navbar {
|
||||||
|
background: var(--navbar-bg) !important;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link,
|
||||||
|
.dropdown-item {
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background: var(--input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item.text-danger {
|
||||||
|
color: var(--danger) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cards ── */
|
||||||
|
.discord-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Inputs ── */
|
||||||
|
.form-control,
|
||||||
|
.form-control:focus {
|
||||||
|
background: var(--input-bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
outline: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Buttons ── */
|
||||||
|
.btn-accent {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accent:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-discord {
|
||||||
|
background: var(--danger);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-discord:hover {
|
||||||
|
background: #c23235;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Price tag ── */
|
||||||
|
.price-tag {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
/* Slightly larger for the wider card */
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--accent);
|
||||||
|
/* Optional: uses your accent color for better visibility */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Table ── */
|
||||||
|
.table {
|
||||||
|
color: var(--text-main);
|
||||||
|
--bs-table-color: var(--text-main);
|
||||||
|
--bs-table-bg: transparent;
|
||||||
|
--bs-table-border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- Checkbox Size Fix -- */
|
||||||
|
#select-all {
|
||||||
|
transform: scale(1.3);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .table {
|
||||||
|
--bs-table-color: var(--text-main);
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
background: var(--table-head);
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Bulk bar ── */
|
||||||
|
.bulk-bar {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-bar .form-control {
|
||||||
|
width: 110px;
|
||||||
|
background: rgba(0, 0, 0, 0.2) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-bar .form-control::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── New-product prompt ── */
|
||||||
|
.new-product-prompt {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Product image ── */
|
||||||
|
#display-img {
|
||||||
|
width: 100%;
|
||||||
|
/* Allows it to fill the new width */
|
||||||
|
max-width: 250px;
|
||||||
|
/* Increased from 160px */
|
||||||
|
height: auto;
|
||||||
|
max-height: 250px;
|
||||||
|
/* Increased from 160px */
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Checkbox ── */
|
||||||
|
input[type="checkbox"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Mobile: hide barcode column ── */
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.col-barcode {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-edit-sm,
|
||||||
|
.btn-del-sm {
|
||||||
|
padding: 4px 7px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header,
|
||||||
|
.modal-footer {
|
||||||
|
border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
/* Makes the X button visible in dark mode */
|
||||||
|
filter: var(--bs-theme-placeholder, invert(0.7) grayscale(100%) brightness(200%));
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .btn-close {
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add this inside your <style> tag */
|
||||||
|
[data-theme="dark"] .text-muted {
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .modal-body .text-muted {
|
||||||
|
color: #f6f6f7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .btn-close {
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select {
|
||||||
|
background-color: var(--input-bg) !important;
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
border: none !important;
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
background-position: right 0.75rem center !important;
|
||||||
|
background-size: 16px 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dcddde' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
|
||||||
|
}
|
||||||
39
static/themeStuff.js
Normal file
39
static/themeStuff.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
function applyTheme(t) {
|
||||||
|
document.documentElement.setAttribute('data-theme', t);
|
||||||
|
localStorage.setItem('theme', t);
|
||||||
|
|
||||||
|
const isDark = (t === 'dark');
|
||||||
|
const themeIcon = document.getElementById('theme-icon');
|
||||||
|
const themeLabel = document.getElementById('theme-label');
|
||||||
|
|
||||||
|
if (themeIcon) {
|
||||||
|
themeIcon.className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
||||||
|
}
|
||||||
|
if (themeLabel) {
|
||||||
|
themeLabel.innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
const current = document.documentElement.getAttribute('data-theme');
|
||||||
|
applyTheme(current === 'dark' ? 'light' : 'dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTheme() {
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
if (savedTheme) {
|
||||||
|
applyTheme(savedTheme);
|
||||||
|
} else {
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
applyTheme(prefersDark ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for system theme changes only if the user hasn't set a manual override
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||||
|
if (!localStorage.getItem('theme')) {
|
||||||
|
applyTheme(e.matches ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
initTheme();
|
||||||
@@ -121,7 +121,8 @@
|
|||||||
|
|
||||||
.form-control::placeholder {
|
.form-control::placeholder {
|
||||||
color: var(--text-muted) !important;
|
color: var(--text-muted) !important;
|
||||||
opacity: 1; /* Forces Firefox to respect the color */
|
opacity: 1;
|
||||||
|
/* Forces Firefox to respect the color */
|
||||||
}
|
}
|
||||||
|
|
||||||
#grand-total {
|
#grand-total {
|
||||||
@@ -191,14 +192,18 @@
|
|||||||
|
|
||||||
/* ── Thermal Printer Styles (80mm) ── */
|
/* ── Thermal Printer Styles (80mm) ── */
|
||||||
@media print {
|
@media print {
|
||||||
|
|
||||||
/* Kill all animations instantly so the printer doesn't photograph a mid-fade background */
|
/* Kill all animations instantly so the printer doesn't photograph a mid-fade background */
|
||||||
*, *::before, *::after {
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Force true white background */
|
/* Force true white background */
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
background: #ffffff !important;
|
background: #ffffff !important;
|
||||||
color: #000000 !important;
|
color: #000000 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
@@ -207,7 +212,11 @@
|
|||||||
print-color-adjust: exact !important;
|
print-color-adjust: exact !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar, .container-fluid, .modal { display: none !important; }
|
.navbar,
|
||||||
|
.container-fluid,
|
||||||
|
.modal {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
#receipt-print-zone {
|
#receipt-print-zone {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
@@ -229,13 +238,36 @@
|
|||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@page { margin: 0; }
|
@page {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.receipt-header { text-align: center; margin-bottom: 10px; }
|
.receipt-header {
|
||||||
.receipt-table { width: 100%; margin-bottom: 10px; }
|
text-align: center;
|
||||||
.receipt-table th { text-align: left; border-bottom: 1px dashed #000 !important; padding-bottom: 3px; }
|
margin-bottom: 10px;
|
||||||
.receipt-table td { padding: 3px 0; vertical-align: top; }
|
}
|
||||||
.receipt-total-row { border-top: 1px dashed #000 !important; font-weight: 800 !important; font-size: 14px; }
|
|
||||||
|
.receipt-table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-table th {
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px dashed #000 !important;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-table td {
|
||||||
|
padding: 3px 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.receipt-total-row {
|
||||||
|
border-top: 1px dashed #000 !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Dropdown Select Fix ── */
|
/* ── Dropdown Select Fix ── */
|
||||||
@@ -259,7 +291,9 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="modal fade" id="customProductModal" tabindex="-1" data-bs-backdrop="static">
|
{% with active_page='checkout' %}{% include 'navbar.html' %}{% endwith %}
|
||||||
|
|
||||||
|
<div class="modal fade" id="customProductModal" tabindex="-1" data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -311,7 +345,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="receipt-items-print">
|
<tbody id="receipt-items-print">
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="receipt-total-row d-flex justify-content-between pt-2">
|
<div class="receipt-total-row d-flex justify-content-between pt-2">
|
||||||
@@ -375,18 +409,23 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered modal-sm">
|
<div class="modal-dialog modal-dialog-centered modal-sm">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header border-0 pb-0">
|
<div class="modal-header border-0 pb-0">
|
||||||
<h5 class="modal-title w-100 text-center text-muted text-uppercase" style="font-size: 0.8rem;">Total a Pagar</h5>
|
<h5 class="modal-title w-100 text-center text-muted text-uppercase" style="font-size: 0.8rem;">Total
|
||||||
<button type="button" class="btn-close position-absolute end-0 top-0 m-3" data-bs-dismiss="modal"></button>
|
a Pagar</h5>
|
||||||
|
<button type="button" class="btn-close position-absolute end-0 top-0 m-3"
|
||||||
|
data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body text-center pt-1 pb-4">
|
<div class="modal-body text-center pt-1 pb-4">
|
||||||
<h1 id="payment-modal-total" class="mb-4" style="color: var(--accent); font-weight: 800; font-size: 3rem;">$0</h1>
|
<h1 id="payment-modal-total" class="mb-4"
|
||||||
|
style="color: var(--accent); font-weight: 800; font-size: 3rem;">$0</h1>
|
||||||
|
|
||||||
<div class="d-grid gap-3 px-3">
|
<div class="d-grid gap-3 px-3">
|
||||||
<button class="btn btn-lg btn-success py-3" onclick="openVueltoModal()">
|
<button class="btn btn-lg btn-success py-3" onclick="openVueltoModal()">
|
||||||
<i class="bi bi-cash-coin me-2" style="font-size: 1.5rem; vertical-align: middle;"></i> Efectivo
|
<i class="bi bi-cash-coin me-2" style="font-size: 1.5rem; vertical-align: middle;"></i>
|
||||||
|
Efectivo
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-lg btn-secondary py-3" onclick="executeCheckout('tarjeta')">
|
<button class="btn btn-lg btn-secondary py-3" onclick="executeCheckout('tarjeta')">
|
||||||
<i class="bi bi-credit-card me-2" style="font-size: 1.5rem; vertical-align: middle;"></i> Tarjeta (Pronto)
|
<i class="bi bi-credit-card me-2" style="font-size: 1.5rem; vertical-align: middle;"></i>
|
||||||
|
Tarjeta (Pronto)
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -397,8 +436,10 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered modal-sm">
|
<div class="modal-dialog modal-dialog-centered modal-sm">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header border-0 pb-0">
|
<div class="modal-header border-0 pb-0">
|
||||||
<h5 class="modal-title w-100 text-center text-muted text-uppercase" style="font-size: 0.8rem;">Pago en Efectivo</h5>
|
<h5 class="modal-title w-100 text-center text-muted text-uppercase" style="font-size: 0.8rem;">Pago
|
||||||
<button type="button" class="btn-close position-absolute end-0 top-0 m-3" data-bs-dismiss="modal"></button>
|
en Efectivo</h5>
|
||||||
|
<button type="button" class="btn-close position-absolute end-0 top-0 m-3"
|
||||||
|
data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body text-center pt-1 pb-4">
|
<div class="modal-body text-center pt-1 pb-4">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -408,8 +449,9 @@
|
|||||||
|
|
||||||
<div class="mb-3 text-start">
|
<div class="mb-3 text-start">
|
||||||
<label class="text-muted small mb-1">Monto Recibido</label>
|
<label class="text-muted small mb-1">Monto Recibido</label>
|
||||||
<input type="number" id="monto-recibido" class="form-control form-control-lg text-center fw-bold fs-4"
|
<input type="number" id="monto-recibido"
|
||||||
placeholder="$0" onkeyup="calculateVuelto()" onchange="calculateVuelto()"
|
class="form-control form-control-lg text-center fw-bold fs-4" placeholder="$0"
|
||||||
|
onkeyup="calculateVuelto()" onchange="calculateVuelto()"
|
||||||
onkeydown="if(event.key === 'Enter' && !document.getElementById('btn-confirm-vuelto').disabled) executeCheckout('efectivo')">
|
onkeydown="if(event.key === 'Enter' && !document.getElementById('btn-confirm-vuelto').disabled) executeCheckout('efectivo')">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -420,7 +462,8 @@
|
|||||||
<span id="vuelto-amount" class="fs-1 fw-bold text-muted">$0</span>
|
<span id="vuelto-amount" class="fs-1 fw-bold text-muted">$0</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="btn-confirm-vuelto" class="btn btn-success w-100 py-3 fw-bold" onclick="executeCheckout('efectivo')" disabled>
|
<button id="btn-confirm-vuelto" class="btn btn-success w-100 py-3 fw-bold"
|
||||||
|
onclick="executeCheckout('efectivo')" disabled>
|
||||||
Confirmar Venta
|
Confirmar Venta
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -436,7 +479,8 @@
|
|||||||
<div class="modal-body text-center pt-0 pb-4">
|
<div class="modal-body text-center pt-0 pb-4">
|
||||||
<i class="bi bi-question-circle text-warning mb-3" style="font-size: 3rem;"></i>
|
<i class="bi bi-question-circle text-warning mb-3" style="font-size: 3rem;"></i>
|
||||||
<h4 class="mb-2">Producto No Registrado</h4>
|
<h4 class="mb-2">Producto No Registrado</h4>
|
||||||
<p class="text-muted px-3 small">El código <strong id="not-found-barcode" style="color: var(--text-main);"></strong> no existe en la base de datos.</p>
|
<p class="text-muted px-3 small">El código <strong id="not-found-barcode"
|
||||||
|
style="color: var(--text-main);"></strong> no existe en la base de datos.</p>
|
||||||
|
|
||||||
<div class="d-flex flex-column gap-2 px-3 mt-4">
|
<div class="d-flex flex-column gap-2 px-3 mt-4">
|
||||||
<button class="btn btn-accent w-100 py-2" onclick="goToInventory()">
|
<button class="btn btn-accent w-100 py-2" onclick="goToInventory()">
|
||||||
@@ -450,48 +494,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
|
||||||
<span class="navbar-brand">SekiPOS <small class="text-muted fw-normal"
|
|
||||||
style="font-size:0.65rem;">Caja</small></span>
|
|
||||||
|
|
||||||
<div class="ms-3 gap-2 d-flex">
|
|
||||||
<a href="/" class="btn btn-outline-primary btn-sm">
|
|
||||||
<i class="bi bi-box-seam me-1"></i>Inventario
|
|
||||||
</a>
|
|
||||||
<a href="/sales" class="btn btn-outline-primary btn-sm">
|
|
||||||
<i class="bi bi-receipt me-1"></i>Ventas
|
|
||||||
</a>
|
|
||||||
<a href="/dicom" class="btn btn-outline-danger btn-sm">
|
|
||||||
<i class="bi bi-journal-x me-1"></i>Dicom
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ms-auto">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-accent dropdown-toggle" type="button" data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false">
|
|
||||||
<i class="bi bi-person-circle me-1"></i>
|
|
||||||
<span class="d-none d-sm-inline">{{ user.username }}</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end shadow">
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="toggleTheme()">
|
|
||||||
<i class="bi bi-moon-stars me-2" id="theme-icon"></i>
|
|
||||||
<span id="theme-label">Modo Oscuro</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<hr class="dropdown-divider" style="border-color: var(--border);">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item text-danger" href="/logout">
|
|
||||||
<i class="bi bi-box-arrow-right me-2"></i>Salir
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
@@ -500,17 +502,20 @@
|
|||||||
<h4><i class="bi bi-cart3"></i> Carrito</h4>
|
<h4><i class="bi bi-cart3"></i> Carrito</h4>
|
||||||
<div class="position-relative mb-4">
|
<div class="position-relative mb-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text border-0 position-absolute" style="background: transparent; z-index: 10;">
|
<span class="input-group-text border-0 position-absolute"
|
||||||
|
style="background: transparent; z-index: 10;">
|
||||||
<i class="bi bi-search text-muted"></i>
|
<i class="bi bi-search text-muted"></i>
|
||||||
</span>
|
</span>
|
||||||
<input type="text" id="manual-search" class="form-control ps-5 py-2 rounded"
|
<input type="text" id="manual-search" class="form-control ps-5 py-2 rounded"
|
||||||
placeholder="Buscar producto por nombre o código..." autocomplete="off" onkeyup="filterSearch()">
|
placeholder="Buscar producto por nombre o código..." autocomplete="off"
|
||||||
<button class="btn btn-accent px-3" type="button" onclick="openCustomProductModal()" title="Agregar manual">
|
onkeyup="filterSearch()">
|
||||||
|
<button class="btn btn-accent px-3" type="button" onclick="openCustomProductModal()"
|
||||||
|
title="Agregar manual">
|
||||||
<i class="bi bi-plus-lg"></i> <span class="d-none d-sm-inline ms-1">Manual</span>
|
<i class="bi bi-plus-lg"></i> <span class="d-none d-sm-inline ms-1">Manual</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="search-results" class="dropdown-menu w-100 shadow-sm mt-1"
|
<div id="search-results" class="dropdown-menu w-100 shadow-sm mt-1"
|
||||||
style="display: none; position: absolute; top: 100%; left: 0; z-index: 1000; max-height: 300px; overflow-y: auto;">
|
style="display: none; position: absolute; top: 100%; left: 0; z-index: 1000; max-height: 300px; overflow-y: auto;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="table mt-3" id="cart-table">
|
<table class="table mt-3" id="cart-table">
|
||||||
@@ -811,37 +816,18 @@
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyTheme(t) {
|
|
||||||
document.documentElement.setAttribute('data-theme', t);
|
|
||||||
const isDark = t === 'dark';
|
|
||||||
const themeIcon = document.getElementById('theme-icon');
|
|
||||||
const themeLabel = document.getElementById('theme-label');
|
|
||||||
|
|
||||||
if (themeIcon) themeIcon.className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
|
||||||
if (themeLabel) themeLabel.innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
|
||||||
|
|
||||||
localStorage.setItem('theme', t);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
|
||||||
const current = document.documentElement.getAttribute('data-theme');
|
|
||||||
const next = current === 'dark' ? 'light' : 'dark';
|
|
||||||
applyTheme(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 1. Load all products from Python into a JavaScript array safely
|
// 1. Load all products from Python into a JavaScript array safely
|
||||||
const allProducts = [
|
const allProducts = [
|
||||||
{% for p in products %}
|
{% for p in products %}
|
||||||
{
|
{
|
||||||
barcode: {{ p[0] | tojson }},
|
barcode: { { p[0] | tojson } },
|
||||||
name: {{ p[1] | tojson }},
|
name: { { p[1] | tojson } },
|
||||||
price: {{ p[2] | int }},
|
price: { { p[2] | int } },
|
||||||
image: {{ p[3] | tojson }},
|
image: { { p[3] | tojson } },
|
||||||
stock: {{ p[4] | int }},
|
stock: { { p[4] | int } },
|
||||||
unit: {{ p[5] | tojson }}
|
unit: { { p[5] | tojson } }
|
||||||
},
|
},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
];
|
];
|
||||||
|
|
||||||
// 2. Extracted this into a helper so both Scanner and Search can use it
|
// 2. Extracted this into a helper so both Scanner and Search can use it
|
||||||
@@ -922,7 +908,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close the search dropdown if user clicks outside of it
|
// Close the search dropdown if user clicks outside of it
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function (e) {
|
||||||
const searchArea = document.getElementById('manual-search');
|
const searchArea = document.getElementById('manual-search');
|
||||||
const resultsBox = document.getElementById('search-results');
|
const resultsBox = document.getElementById('search-results');
|
||||||
if (e.target !== searchArea && !resultsBox.contains(e.target)) {
|
if (e.target !== searchArea && !resultsBox.contains(e.target)) {
|
||||||
@@ -953,7 +939,7 @@
|
|||||||
try {
|
try {
|
||||||
cart = JSON.parse(saved);
|
cart = JSON.parse(saved);
|
||||||
renderCart();
|
renderCart();
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
console.error("Error cargando el carrito", e);
|
console.error("Error cargando el carrito", e);
|
||||||
cart = [];
|
cart = [];
|
||||||
}
|
}
|
||||||
@@ -970,7 +956,7 @@
|
|||||||
modal.show();
|
modal.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function printReceipt(total, saleId) {
|
function printReceipt(total, saleId) {
|
||||||
const tbody = document.getElementById('receipt-items-print');
|
const tbody = document.getElementById('receipt-items-print');
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
@@ -1107,27 +1093,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize on load
|
|
||||||
(function initTheme() {
|
|
||||||
const savedTheme = localStorage.getItem('theme');
|
|
||||||
if (savedTheme) {
|
|
||||||
applyTheme(savedTheme);
|
|
||||||
} else {
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
applyTheme(prefersDark ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Listen for OS theme changes in real-time
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
applyTheme(e.matches ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
loadCart();
|
loadCart();
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="./static/cookieStuff.js"></script>
|
||||||
|
<script src="./static/themeStuff.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es" data-theme="light">
|
<html lang="es" data-theme="light">
|
||||||
|
|
||||||
<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">
|
||||||
@@ -9,69 +10,124 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #ebedef; --card-bg: #ffffff; --text-main: #2e3338;
|
--bg: #ebedef;
|
||||||
--text-muted: #4f5660; --border: #e3e5e8; --navbar-bg: #ffffff;
|
--card-bg: #ffffff;
|
||||||
--accent: #5865f2; --input-bg: #e3e5e8; --danger: #ed4245;
|
--text-main: #2e3338;
|
||||||
|
--text-muted: #4f5660;
|
||||||
|
--border: #e3e5e8;
|
||||||
|
--navbar-bg: #ffffff;
|
||||||
|
--accent: #5865f2;
|
||||||
|
--input-bg: #e3e5e8;
|
||||||
|
--danger: #ed4245;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--bg: #36393f; --card-bg: #2f3136; --text-main: #dcddde;
|
--bg: #36393f;
|
||||||
--text-muted: #b9bbbe; --border: #202225; --navbar-bg: #202225;
|
--card-bg: #2f3136;
|
||||||
|
--text-main: #dcddde;
|
||||||
|
--text-muted: #b9bbbe;
|
||||||
|
--border: #202225;
|
||||||
|
--navbar-bg: #202225;
|
||||||
--input-bg: #202225;
|
--input-bg: #202225;
|
||||||
}
|
}
|
||||||
|
|
||||||
body { background: var(--bg); color: var(--text-main); font-family: "gg sans", "Segoe UI", sans-serif; transition: background 0.2s; }
|
body {
|
||||||
.navbar { background: var(--navbar-bg) !important; border-bottom: 1px solid var(--border); }
|
background: var(--bg);
|
||||||
.navbar-brand { color: var(--text-main) !important; font-weight: 700; }
|
color: var(--text-main);
|
||||||
|
font-family: "gg sans", "Segoe UI", sans-serif;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
.discord-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
.navbar {
|
||||||
|
background: var(--navbar-bg) !important;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
.form-control, .form-control:focus { background-color: var(--input-bg) !important; color: var(--text-main) !important; border: 1px solid var(--border) !important; box-shadow: none !important; }
|
.navbar-brand {
|
||||||
.form-control:focus { border-color: var(--accent) !important; }
|
color: var(--text-main) !important;
|
||||||
.form-control::placeholder { color: var(--text-muted) !important; opacity: 1; }
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.table { color: var(--text-main) !important; --bs-table-bg: transparent; --bs-table-border-color: var(--border); }
|
.discord-card {
|
||||||
.table thead th { background: var(--bg); color: var(--text-muted); font-size: 0.75rem; text-transform: uppercase; border-bottom: 1px solid var(--border); }
|
background: var(--card-bg);
|
||||||
.table td { border-bottom: 1px solid var(--border); vertical-align: middle; }
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-accent { background: var(--accent); color: #fff; border: none; }
|
.form-control,
|
||||||
.dropdown-menu { background: var(--card-bg); border: 1px solid var(--border); }
|
.form-control:focus {
|
||||||
.dropdown-item { color: var(--text-main) !important; }
|
background-color: var(--input-bg) !important;
|
||||||
.dropdown-item:hover { background: var(--input-bg); }
|
color: var(--text-main) !important;
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .table { --bs-table-color: var(--text-main); color: var(--text-main) !important; }
|
.form-control:focus {
|
||||||
[data-theme="dark"] .table thead th { background: #292b2f; color: var(--text-muted); }
|
border-color: var(--accent) !important;
|
||||||
[data-theme="dark"] .text-muted { color: var(--text-muted) !important; }
|
}
|
||||||
|
|
||||||
|
.form-control::placeholder {
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
--bs-table-bg: transparent;
|
||||||
|
--bs-table-border-color: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accent {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background: var(--input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .table {
|
||||||
|
--bs-table-color: var(--text-main);
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .table thead th {
|
||||||
|
background: #292b2f;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .text-muted {
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
{% with active_page='dicom' %}{% include 'navbar.html' %}{% endwith %}
|
||||||
<span class="navbar-brand">SekiPOS <small class="text-danger fw-bold ms-1" style="font-size:0.75rem;"><i class="bi bi-journal-x"></i> Dicom</small></span>
|
|
||||||
|
|
||||||
<div class="ms-3 gap-2 d-flex">
|
|
||||||
<a href="/" class="btn btn-outline-primary btn-sm"><i class="bi bi-box-seam me-1"></i>Inventario</a>
|
|
||||||
<a href="/checkout" class="btn btn-outline-primary btn-sm"><i class="bi bi-cart-fill me-1"></i>Caja</a>
|
|
||||||
<a href="/sales" class="btn btn-outline-primary btn-sm"><i class="bi bi-receipt me-1"></i>Ventas</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ms-auto">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-accent dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
<i class="bi bi-person-circle me-1"></i>
|
|
||||||
<span class="d-none d-sm-inline">{{ user.username }}</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end shadow">
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="toggleTheme()">
|
|
||||||
<i class="bi bi-moon-stars me-2" id="theme-icon"></i>
|
|
||||||
<span id="theme-label">Modo Oscuro</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li><hr class="dropdown-divider" style="border-color: var(--border);"></li>
|
|
||||||
<li><a class="dropdown-item text-danger" href="/logout"><i class="bi bi-box-arrow-right me-2"></i>Salir</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container-fluid px-3">
|
<div class="container-fluid px-3">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
@@ -80,7 +136,8 @@
|
|||||||
<div class="discord-card p-3 mb-3">
|
<div class="discord-card p-3 mb-3">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<h5 class="mb-0 fw-bold">Registrar Movimiento</h5>
|
<h5 class="mb-0 fw-bold">Registrar Movimiento</h5>
|
||||||
<button class="btn btn-sm btn-outline-secondary" onclick="clearDicomForm()" title="Limpiar Formulario">
|
<button class="btn btn-sm btn-outline-secondary" onclick="clearDicomForm()"
|
||||||
|
title="Limpiar Formulario">
|
||||||
<i class="bi bi-eraser"></i> Nuevo
|
<i class="bi bi-eraser"></i> Nuevo
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +151,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="small text-muted mb-1">Nota (Opcional)</label>
|
<label class="small text-muted mb-1">Nota (Opcional)</label>
|
||||||
<input type="text" id="dicom-notes" class="form-control" placeholder="Ej: Pan y bebida" onkeydown="if(event.key === 'Enter') submitDicom('add')">
|
<input type="text" id="dicom-notes" class="form-control" placeholder="Ej: Pan y bebida"
|
||||||
|
onkeydown="if(event.key === 'Enter') submitDicom('add')">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-column gap-2">
|
<div class="d-flex flex-column gap-2">
|
||||||
@@ -112,7 +170,8 @@
|
|||||||
<div class="discord-card p-3">
|
<div class="discord-card p-3">
|
||||||
|
|
||||||
<div class="position-relative mb-3">
|
<div class="position-relative mb-3">
|
||||||
<input type="text" id="dicom-search" class="form-control ps-5" placeholder="Buscar cliente por nombre..." onkeyup="filterDicom()">
|
<input type="text" id="dicom-search" class="form-control ps-5"
|
||||||
|
placeholder="Buscar cliente por nombre..." onkeyup="filterDicom()">
|
||||||
<i class="bi bi-search position-absolute top-50 start-0 translate-middle-y ms-3 text-muted"></i>
|
<i class="bi bi-search position-absolute top-50 start-0 translate-middle-y ms-3 text-muted"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -135,10 +194,12 @@
|
|||||||
<td class="text-muted small">{{ d[3] }}</td>
|
<td class="text-muted small">{{ d[3] }}</td>
|
||||||
<td class="text-muted small">{{ d[4] }}</td>
|
<td class="text-muted small">{{ d[4] }}</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<button class="btn btn-sm btn-outline-secondary" onclick="selectClient('{{ d[1] }}')" title="Seleccionar">
|
<button class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="selectClient('{{ d[1] }}')" title="Seleccionar">
|
||||||
<i class="bi bi-pencil"></i>
|
<i class="bi bi-pencil"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-outline-danger ms-1" onclick="forgiveDebt({{ d[0] }}, '{{ d[1] }}')" title="Eliminar Registro">
|
<button class="btn btn-sm btn-outline-danger ms-1"
|
||||||
|
onclick="forgiveDebt({{ d[0] }}, '{{ d[1] }}')" title="Eliminar Registro">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -228,40 +289,9 @@
|
|||||||
const res = await fetch(`/api/dicom/${id}`, { method: 'DELETE' });
|
const res = await fetch(`/api/dicom/${id}`, { method: 'DELETE' });
|
||||||
if (res.ok) window.location.reload();
|
if (res.ok) window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Theme Management ── */
|
|
||||||
function applyTheme(t) {
|
|
||||||
document.documentElement.setAttribute('data-theme', t);
|
|
||||||
const isDark = t === 'dark';
|
|
||||||
const themeIcon = document.getElementById('theme-icon');
|
|
||||||
const themeLabel = document.getElementById('theme-label');
|
|
||||||
|
|
||||||
if (themeIcon) themeIcon.className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
|
||||||
if (themeLabel) themeLabel.innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
|
||||||
|
|
||||||
localStorage.setItem('theme', t);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
|
||||||
const current = document.documentElement.getAttribute('data-theme');
|
|
||||||
applyTheme(current === 'dark' ? 'light' : 'dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
(function initTheme() {
|
|
||||||
const savedTheme = localStorage.getItem('theme');
|
|
||||||
if (savedTheme) {
|
|
||||||
applyTheme(savedTheme);
|
|
||||||
} else {
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
applyTheme(prefersDark ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
applyTheme(e.matches ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
<script src="./static/cookieStuff.js"></script>
|
||||||
|
<script src="./static/themeStuff.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -10,312 +10,11 @@
|
|||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
<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>
|
||||||
<script src="https://unpkg.com/html5-qrcode"></script>
|
<script src="https://unpkg.com/html5-qrcode"></script>
|
||||||
<style>
|
<link rel="stylesheet" href="./static/style.css">
|
||||||
:root {
|
|
||||||
--bg: #ebedef;
|
|
||||||
--card-bg: #ffffff;
|
|
||||||
--text-main: #2e3338;
|
|
||||||
--text-muted: #4f5660;
|
|
||||||
--border: #e3e5e8;
|
|
||||||
--navbar-bg: #ffffff;
|
|
||||||
--input-bg: #e3e5e8;
|
|
||||||
--table-head: #f2f3f5;
|
|
||||||
--accent: #5865f2;
|
|
||||||
--accent-hover: #4752c4;
|
|
||||||
--danger: #ed4245;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] {
|
|
||||||
--bg: #36393f;
|
|
||||||
--card-bg: #2f3136;
|
|
||||||
--text-main: #dcddde;
|
|
||||||
--text-muted: #b9bbbe;
|
|
||||||
--border: #202225;
|
|
||||||
--navbar-bg: #202225;
|
|
||||||
--input-bg: #202225;
|
|
||||||
--table-head: #292b2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text-main);
|
|
||||||
font-family: "gg sans", "Segoe UI", sans-serif;
|
|
||||||
transition: background 0.2s, color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Navbar ── */
|
|
||||||
.navbar {
|
|
||||||
background: var(--navbar-bg) !important;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
color: var(--text-main) !important;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link,
|
|
||||||
.dropdown-item {
|
|
||||||
color: var(--text-main) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item:hover {
|
|
||||||
background: var(--input-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item.text-danger {
|
|
||||||
color: var(--danger) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Cards ── */
|
|
||||||
.discord-card {
|
|
||||||
background: var(--card-bg);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Inputs ── */
|
|
||||||
.form-control,
|
|
||||||
.form-control:focus {
|
|
||||||
background: var(--input-bg);
|
|
||||||
color: var(--text-main);
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:focus {
|
|
||||||
outline: 2px solid var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control::placeholder {
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Buttons ── */
|
|
||||||
.btn-accent {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-accent:hover {
|
|
||||||
background: var(--accent-hover);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger-discord {
|
|
||||||
background: var(--danger);
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger-discord:hover {
|
|
||||||
background: #c23235;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Price tag ── */
|
|
||||||
.price-tag {
|
|
||||||
font-size: 2.8rem;
|
|
||||||
/* Slightly larger for the wider card */
|
|
||||||
font-weight: 800;
|
|
||||||
color: var(--accent);
|
|
||||||
/* Optional: uses your accent color for better visibility */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Table ── */
|
|
||||||
.table {
|
|
||||||
color: var(--text-main);
|
|
||||||
--bs-table-color: var(--text-main);
|
|
||||||
--bs-table-bg: transparent;
|
|
||||||
--bs-table-border-color: var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- Checkbox Size Fix -- */
|
|
||||||
#select-all {
|
|
||||||
transform: scale(1.3);
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .table {
|
|
||||||
--bs-table-color: var(--text-main);
|
|
||||||
color: var(--text-main);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table thead th {
|
|
||||||
background: var(--table-head);
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-size: 0.72rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table tbody td {
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Bulk bar ── */
|
|
||||||
.bulk-bar {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bulk-bar .form-control {
|
|
||||||
width: 110px;
|
|
||||||
background: rgba(0, 0, 0, 0.2) !important;
|
|
||||||
color: #fff !important;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bulk-bar .form-control::placeholder {
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── New-product prompt ── */
|
|
||||||
.new-product-prompt {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Product image ── */
|
|
||||||
#display-img {
|
|
||||||
width: 100%;
|
|
||||||
/* Allows it to fill the new width */
|
|
||||||
max-width: 250px;
|
|
||||||
/* Increased from 160px */
|
|
||||||
height: auto;
|
|
||||||
max-height: 250px;
|
|
||||||
/* Increased from 160px */
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Checkbox ── */
|
|
||||||
input[type="checkbox"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Mobile: hide barcode column ── */
|
|
||||||
@media (max-width: 576px) {
|
|
||||||
.col-barcode {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-edit-sm,
|
|
||||||
.btn-del-sm {
|
|
||||||
padding: 4px 7px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
background: var(--card-bg);
|
|
||||||
color: var(--text-main);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header,
|
|
||||||
.modal-footer {
|
|
||||||
border-color: var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-close {
|
|
||||||
/* Makes the X button visible in dark mode */
|
|
||||||
filter: var(--bs-theme-placeholder, invert(0.7) grayscale(100%) brightness(200%));
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .btn-close {
|
|
||||||
filter: invert(1) grayscale(100%) brightness(200%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this inside your <style> tag */
|
|
||||||
[data-theme="dark"] .text-muted {
|
|
||||||
color: var(--text-muted) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .modal-body .text-muted {
|
|
||||||
color: #f6f6f7 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-close {
|
|
||||||
filter: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .btn-close {
|
|
||||||
filter: invert(1) grayscale(100%) brightness(200%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix for the missing dropdown arrow */
|
|
||||||
.form-select {
|
|
||||||
background-color: var(--input-bg) !important;
|
|
||||||
color: var(--text-main) !important;
|
|
||||||
border: none !important;
|
|
||||||
/* This ensures the arrow icon is still rendered over the custom background */
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
|
|
||||||
background-repeat: no-repeat !important;
|
|
||||||
background-position: right 0.75rem center !important;
|
|
||||||
background-size: 16px 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .form-select {
|
|
||||||
/* Lighter arrow for dark mode */
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dcddde' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
{% with active_page='inventory' %}{% include 'navbar.html' %}{% endwith %}
|
||||||
<!-- ════════════════════════ NAVBAR ════════════════════════ -->
|
|
||||||
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
|
||||||
<span class="navbar-brand">SekiPOS <small class="text-muted fw-normal"
|
|
||||||
style="font-size:0.65rem;">v1.6</small></span>
|
|
||||||
<div class="ms-3 gap-2 d-flex">
|
|
||||||
<a href="/checkout" class="btn btn-outline-primary btn-sm">
|
|
||||||
<i class="bi bi-cart-fill me-1"></i>Caja
|
|
||||||
</a>
|
|
||||||
<a href="/sales" class="btn btn-outline-primary btn-sm">
|
|
||||||
<i class="bi bi-receipt me-1"></i>Ventas
|
|
||||||
</a>
|
|
||||||
<a href="/dicom" class="btn btn-outline-danger btn-sm">
|
|
||||||
<i class="bi bi-journal-x me-1"></i>Dicom
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<!-- Always-visible dropdown on the right -->
|
|
||||||
<div class="ms-auto">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-accent dropdown-toggle" type="button" data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false">
|
|
||||||
<i class="bi bi-person-circle me-1"></i>
|
|
||||||
<span class="d-none d-sm-inline">{{ user.username }}</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end">
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="toggleTheme()">
|
|
||||||
<i class="bi bi-moon-stars me-2" id="theme-icon"></i>
|
|
||||||
<span id="theme-label">Modo Oscuro</span>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<hr class="dropdown-divider" style="border-color: var(--border);">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item text-danger" href="/logout">
|
|
||||||
<i class="bi bi-box-arrow-right me-2"></i>Salir
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- ════════════════════════ MAIN ════════════════════════ -->
|
<!-- ════════════════════════ MAIN ════════════════════════ -->
|
||||||
<div class="container-fluid px-3">
|
<div class="container-fluid px-3">
|
||||||
@@ -453,9 +152,10 @@
|
|||||||
<td class="name-cell">{{ p[1] }}</td>
|
<td class="name-cell">{{ p[1] }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if p[5] == 'kg' %}
|
{% if p[5] == 'kg' %}
|
||||||
<span class="text-muted d-inline-block text-center" style="width: 45px;">-</span>
|
<span class="text-muted d-inline-block text-center"
|
||||||
|
style="width: 45px;">-</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ p[4] | int }} <small class="text-muted">Uni</small>
|
{{ p[4] | int }} <small class="text-muted">Uni</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="price-cell" data-value="{{ p[2] }}"></td>
|
<td class="price-cell" data-value="{{ p[2] }}"></td>
|
||||||
@@ -597,20 +297,6 @@
|
|||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
/* ── Theme ── */
|
|
||||||
function applyTheme(t) {
|
|
||||||
document.documentElement.setAttribute('data-theme', t);
|
|
||||||
const isDark = t === 'dark';
|
|
||||||
document.getElementById('theme-icon').className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
|
||||||
document.getElementById('theme-label').innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
|
||||||
}
|
|
||||||
function toggleTheme() {
|
|
||||||
const next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
|
||||||
localStorage.setItem('theme', next);
|
|
||||||
applyTheme(next);
|
|
||||||
}
|
|
||||||
applyTheme(localStorage.getItem('theme') || 'light');
|
|
||||||
|
|
||||||
/* ── Socket.IO ── */
|
/* ── Socket.IO ── */
|
||||||
const socket = io();
|
const socket = io();
|
||||||
const clp = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', minimumFractionDigits: 0 });
|
const clp = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', minimumFractionDigits: 0 });
|
||||||
@@ -1109,68 +795,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Theme Management ── */
|
|
||||||
|
|
||||||
// Helper to set a cookie
|
|
||||||
function setCookie(name, value, days = 365) {
|
|
||||||
const d = new Date();
|
|
||||||
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
||||||
let expires = "expires=" + d.toUTCString();
|
|
||||||
document.cookie = name + "=" + value + ";" + expires + ";path=/;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to get a cookie
|
|
||||||
function getCookie(name) {
|
|
||||||
let nameEQ = name + "=";
|
|
||||||
let ca = document.cookie.split(';');
|
|
||||||
for (let i = 0; i < ca.length; i++) {
|
|
||||||
let c = ca[i];
|
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
|
||||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A single, clean theme manager
|
|
||||||
function applyTheme(t) {
|
|
||||||
document.documentElement.setAttribute('data-theme', t);
|
|
||||||
const isDark = t === 'dark';
|
|
||||||
|
|
||||||
// Update UI elements if they exist
|
|
||||||
const themeIcon = document.getElementById('theme-icon');
|
|
||||||
const themeLabel = document.getElementById('theme-label');
|
|
||||||
if (themeIcon) themeIcon.className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
|
||||||
if (themeLabel) themeLabel.innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
|
||||||
|
|
||||||
// Save to localStorage (much more reliable than cookies for this)
|
|
||||||
localStorage.setItem('theme', t);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
|
||||||
const current = document.documentElement.getAttribute('data-theme');
|
|
||||||
const next = current === 'dark' ? 'light' : 'dark';
|
|
||||||
applyTheme(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run this immediately on load
|
|
||||||
(function initTheme() {
|
|
||||||
const savedTheme = localStorage.getItem('theme');
|
|
||||||
if (savedTheme) {
|
|
||||||
applyTheme(savedTheme);
|
|
||||||
} else {
|
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
||||||
applyTheme(prefersDark ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Listen for OS theme changes in real-time if no cookie is set
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
|
||||||
if (!getCookie('theme')) {
|
|
||||||
applyTheme(e.matches ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Function to toggle stock state
|
// Function to toggle stock state
|
||||||
function toggleStockInput() {
|
function toggleStockInput() {
|
||||||
const unitSelect = document.getElementById('form-unit-type');
|
const unitSelect = document.getElementById('form-unit-type');
|
||||||
@@ -1189,8 +813,9 @@
|
|||||||
// Listen for manual dropdown changes
|
// Listen for manual dropdown changes
|
||||||
document.getElementById('form-unit-type').addEventListener('change', toggleStockInput);
|
document.getElementById('form-unit-type').addEventListener('change', toggleStockInput);
|
||||||
|
|
||||||
initTheme();
|
|
||||||
</script>
|
</script>
|
||||||
|
<script src="./static/cookieStuff.js"></script>
|
||||||
|
<script src="./static/themeStuff.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
56
templates/navbar.html
Normal file
56
templates/navbar.html
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
||||||
|
<span class="navbar-brand">
|
||||||
|
SekiPOS
|
||||||
|
{% if active_page == 'checkout' %}
|
||||||
|
<small class="text-muted fw-normal" style="font-size:0.65rem;">v1.6</small>
|
||||||
|
{% elif active_page == 'sales' %}
|
||||||
|
<small class="text-muted fw-normal" style="font-size:0.65rem;">v1.6</small>
|
||||||
|
{% elif active_page == 'dicom' %}
|
||||||
|
<small class="text-muted fw-normal" style="font-size:0.65rem;">v1.6</small>
|
||||||
|
{% else %}
|
||||||
|
<small class="text-muted fw-normal" style="font-size:0.65rem;">v1.6</small>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="ms-3 gap-2 d-flex">
|
||||||
|
<a href="/" class="btn btn-sm {{ 'btn-primary' if active_page == 'inventory' else 'btn-outline-primary' }}">
|
||||||
|
<i class="bi bi-box-seam me-1"></i>Inventario
|
||||||
|
</a>
|
||||||
|
<a href="/checkout"
|
||||||
|
class="btn btn-sm {{ 'btn-primary' if active_page == 'checkout' else 'btn-outline-primary' }}">
|
||||||
|
<i class="bi bi-cart-fill me-1"></i>Caja
|
||||||
|
</a>
|
||||||
|
<a href="/sales" class="btn btn-sm {{ 'btn-primary' if active_page == 'sales' else 'btn-outline-primary' }}">
|
||||||
|
<i class="bi bi-receipt me-1"></i>Ventas
|
||||||
|
</a>
|
||||||
|
<a href="/dicom" class="btn btn-sm {{ 'btn-danger' if active_page == 'dicom' else 'btn-outline-danger' }}">
|
||||||
|
<i class="bi bi-journal-x me-1"></i>Dicom
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ms-auto">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-accent dropdown-toggle" type="button" data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false">
|
||||||
|
<i class="bi bi-person-circle me-1"></i>
|
||||||
|
<span class="d-none d-sm-inline">{{ user.username }}</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end shadow">
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item" onclick="toggleTheme()">
|
||||||
|
<i class="bi bi-moon-stars me-2" id="theme-icon"></i>
|
||||||
|
<span id="theme-label">Modo Oscuro</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<hr class="dropdown-divider" style="border-color: var(--border);">
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item text-danger" href="/logout">
|
||||||
|
<i class="bi bi-box-arrow-right me-2"></i>Salir
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es" data-theme="light">
|
<html lang="es" data-theme="light">
|
||||||
|
|
||||||
<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">
|
||||||
@@ -11,21 +11,60 @@
|
|||||||
<style>
|
<style>
|
||||||
/* Reusing your standard theme colors */
|
/* Reusing your standard theme colors */
|
||||||
:root {
|
:root {
|
||||||
--bg: #ebedef; --card-bg: #ffffff; --text-main: #2e3338;
|
--bg: #ebedef;
|
||||||
--text-muted: #4f5660; --border: #e3e5e8; --navbar-bg: #ffffff;
|
--card-bg: #ffffff;
|
||||||
--accent: #5865f2; --accent-hover: #4752c4; --input-bg: #e3e5e8;
|
--text-main: #2e3338;
|
||||||
|
--text-muted: #4f5660;
|
||||||
|
--border: #e3e5e8;
|
||||||
|
--navbar-bg: #ffffff;
|
||||||
|
--accent: #5865f2;
|
||||||
|
--accent-hover: #4752c4;
|
||||||
|
--input-bg: #e3e5e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--bg: #36393f; --card-bg: #2f3136; --text-main: #dcddde;
|
--bg: #36393f;
|
||||||
--text-muted: #b9bbbe; --border: #202225; --navbar-bg: #202225;
|
--card-bg: #2f3136;
|
||||||
|
--text-main: #dcddde;
|
||||||
|
--text-muted: #b9bbbe;
|
||||||
|
--border: #202225;
|
||||||
|
--navbar-bg: #202225;
|
||||||
--input-bg: #202225;
|
--input-bg: #202225;
|
||||||
}
|
}
|
||||||
body { background: var(--bg); color: var(--text-main); font-family: "gg sans", sans-serif; min-height: 100vh; }
|
|
||||||
.navbar { background: var(--navbar-bg) !important; border-bottom: 1px solid var(--border); }
|
body {
|
||||||
.navbar-brand, .nav-link, .dropdown-item { color: var(--text-main) !important; }
|
background: var(--bg);
|
||||||
.dropdown-menu { background: var(--card-bg); border: 1px solid var(--border); }
|
color: var(--text-main);
|
||||||
.dropdown-item:hover { background: var(--input-bg); }
|
font-family: "gg sans", sans-serif;
|
||||||
.discord-card, .modal-content { background: var(--card-bg) !important; border: 1px solid var(--border); border-radius: 8px; }
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background: var(--navbar-bg) !important;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand,
|
||||||
|
.nav-link,
|
||||||
|
.dropdown-item {
|
||||||
|
color: var(--text-main) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background: var(--input-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.discord-card,
|
||||||
|
.modal-content {
|
||||||
|
background: var(--card-bg) !important;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
color: var(--text-main) !important;
|
color: var(--text-main) !important;
|
||||||
@@ -35,12 +74,14 @@
|
|||||||
/* --- Tables --- */
|
/* --- Tables --- */
|
||||||
.table {
|
.table {
|
||||||
--bs-table-bg: transparent;
|
--bs-table-bg: transparent;
|
||||||
--bs-table-color: var(--text-main); /* This forces Bootstrap to obey */
|
--bs-table-color: var(--text-main);
|
||||||
|
/* This forces Bootstrap to obey */
|
||||||
--bs-table-border-color: var(--border);
|
--bs-table-border-color: var(--border);
|
||||||
--bs-table-hover-color: var(--text-main);
|
--bs-table-hover-color: var(--text-main);
|
||||||
--bs-table-hover-bg: var(--input-bg);
|
--bs-table-hover-bg: var(--input-bg);
|
||||||
color: var(--text-main) !important;
|
color: var(--text-main) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th {
|
.table thead th {
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
@@ -48,48 +89,53 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.table td, .table th {
|
|
||||||
|
.table td,
|
||||||
|
.table th {
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
color: var(--text-main);
|
color: var(--text-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Buttons --- */
|
/* --- Buttons --- */
|
||||||
.btn-accent { background: var(--accent); color: #fff; border: none; }
|
.btn-accent {
|
||||||
.btn-accent:hover { background: var(--accent-hover); color: #fff; }
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accent:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Dark Mode Overrides --- */
|
/* --- Dark Mode Overrides --- */
|
||||||
[data-theme="dark"] .modal-content,
|
[data-theme="dark"] .modal-content,
|
||||||
[data-theme="dark"] .modal-body { color: var(--text-main); }
|
[data-theme="dark"] .modal-body {
|
||||||
[data-theme="dark"] .table { --bs-table-hover-bg: rgba(255, 255, 255, 0.05); }
|
color: var(--text-main);
|
||||||
[data-theme="dark"] .table thead th { background: #292b2f; color: var(--text-muted); }
|
}
|
||||||
[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
|
|
||||||
[data-theme="dark"] .text-muted { color: var(--text-muted) !important; }
|
[data-theme="dark"] .table {
|
||||||
|
--bs-table-hover-bg: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .table thead th {
|
||||||
|
background: #292b2f;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .btn-close {
|
||||||
|
filter: invert(1) grayscale(100%) brightness(200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .text-muted {
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
{% with active_page='sales' %}{% include 'navbar.html' %}{% endwith %}
|
||||||
<span class="navbar-brand">SekiPOS <small class="text-muted fw-normal" style="font-size:0.65rem;">Ventas</small></span>
|
|
||||||
<div class="ms-3 gap-2 d-flex">
|
|
||||||
<a href="/" class="btn btn-outline-primary btn-sm"><i class="bi bi-box-seam me-1"></i>Inventario</a>
|
|
||||||
<a href="/checkout" class="btn btn-outline-primary btn-sm"><i class="bi bi-cart-fill me-1"></i>Caja</a>
|
|
||||||
<a href="/dicom" class="btn btn-outline-danger btn-sm">
|
|
||||||
<i class="bi bi-journal-x me-1"></i>Dicom
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="ms-auto">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-accent dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
|
||||||
<i class="bi bi-person-circle me-1"></i> <span class="d-none d-sm-inline">{{ user.username }}</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end shadow">
|
|
||||||
<li><button class="dropdown-item" onclick="toggleTheme()"><i class="bi bi-moon-stars me-2" id="theme-icon"></i><span id="theme-label">Modo Oscuro</span></button></li>
|
|
||||||
<li><hr class="dropdown-divider" style="border-color: var(--border);"></li>
|
|
||||||
<li><a class="dropdown-item text-danger" href="/logout"><i class="bi bi-box-arrow-right me-2"></i>Salir</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container-fluid px-3">
|
<div class="container-fluid px-3">
|
||||||
|
|
||||||
@@ -99,19 +145,24 @@
|
|||||||
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">
|
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">
|
||||||
{% if selected_date %}Día Seleccionado{% else %}Ventas de Hoy{% endif %}
|
{% if selected_date %}Día Seleccionado{% else %}Ventas de Hoy{% endif %}
|
||||||
</h6>
|
</h6>
|
||||||
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;" data-value="{{ stats.daily }}"></h2>
|
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;"
|
||||||
|
data-value="{{ stats.daily }}"></h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-4">
|
||||||
<div class="discord-card p-3 shadow-sm text-center">
|
<div class="discord-card p-3 shadow-sm text-center">
|
||||||
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">Últimos 7 Días</h6>
|
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">Últimos 7
|
||||||
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;" data-value="{{ stats.week }}"></h2>
|
Días</h6>
|
||||||
|
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;"
|
||||||
|
data-value="{{ stats.week }}"></h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-4">
|
||||||
<div class="discord-card p-3 shadow-sm text-center">
|
<div class="discord-card p-3 shadow-sm text-center">
|
||||||
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">Este Mes</h6>
|
<h6 class="text-muted text-uppercase mb-1" style="font-size: 0.75rem; font-weight: 700;">Este Mes
|
||||||
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;" data-value="{{ stats.month }}"></h2>
|
</h6>
|
||||||
|
<h2 class="price-cell mb-0" style="color: var(--accent); font-weight: 800;"
|
||||||
|
data-value="{{ stats.month }}"></h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,12 +171,14 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<h4 class="mb-0"><i class="bi bi-receipt-cutoff me-2"></i>Historial</h4>
|
<h4 class="mb-0"><i class="bi bi-receipt-cutoff me-2"></i>Historial</h4>
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<label for="date-filter" class="form-label mb-0 text-muted small d-none d-sm-block">Filtrar Día:</label>
|
<label for="date-filter" class="form-label mb-0 text-muted small d-none d-sm-block">Filtrar
|
||||||
|
Día:</label>
|
||||||
<input type="date" id="date-filter" class="form-control form-control-sm"
|
<input type="date" id="date-filter" class="form-control form-control-sm"
|
||||||
style="background: var(--input-bg); color: var(--text-main); border-color: var(--border);"
|
style="background: var(--input-bg); color: var(--text-main); border-color: var(--border);"
|
||||||
value="{{ selected_date or '' }}" onchange="filterByDate(this.value)">
|
value="{{ selected_date or '' }}" onchange="filterByDate(this.value)">
|
||||||
{% if selected_date %}
|
{% if selected_date %}
|
||||||
<a href="/sales" class="btn btn-sm btn-outline-secondary" title="Limpiar filtro"><i class="bi bi-x-lg"></i></a>
|
<a href="/sales" class="btn btn-sm btn-outline-secondary" title="Limpiar filtro"><i
|
||||||
|
class="bi bi-x-lg"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,7 +201,8 @@
|
|||||||
<td class="text-capitalize">{{ s[3] }}</td>
|
<td class="text-capitalize">{{ s[3] }}</td>
|
||||||
<td class="price-cell fw-bold" data-value="{{ s[2] }}"></td>
|
<td class="price-cell fw-bold" data-value="{{ s[2] }}"></td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-outline-secondary" onclick="viewSale({{ s[0] }}, '{{ s[1] }}', {{ s[2] }})">
|
<button class="btn btn-sm btn-outline-secondary"
|
||||||
|
onclick="viewSale({{ s[0] }}, '{{ s[1] }}', {{ s[2] }})">
|
||||||
<i class="bi bi-eye"></i> Ver Detalle
|
<i class="bi bi-eye"></i> Ver Detalle
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -164,7 +218,8 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Detalle de Venta <span id="modal-ticket-id" class="text-muted small"></span></h5>
|
<h5 class="modal-title">Detalle de Venta <span id="modal-ticket-id" class="text-muted small"></span>
|
||||||
|
</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@@ -178,7 +233,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="receipt-items">
|
<tbody id="receipt-items">
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" class="text-end">TOTAL:</th>
|
<th colspan="2" class="text-end">TOTAL:</th>
|
||||||
@@ -188,7 +243,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer d-flex justify-content-between border-0 pt-0">
|
<div class="modal-footer d-flex justify-content-between border-0 pt-0">
|
||||||
<button class="btn btn-outline-danger btn-sm" id="btn-reverse-sale">
|
<button class="btn btn-outline-danger btn-sm" id="btn-reverse-sale">
|
||||||
<i class="bi bi-arrow-counterclockwise me-1"></i>Anular Venta
|
<i class="bi bi-arrow-counterclockwise me-1"></i>Anular Venta
|
||||||
@@ -208,7 +263,8 @@
|
|||||||
<div class="modal-body text-center pt-0 pb-4">
|
<div class="modal-body text-center pt-0 pb-4">
|
||||||
<i class="bi bi-exclamation-triangle-fill text-danger mb-3" style="font-size: 3rem;"></i>
|
<i class="bi bi-exclamation-triangle-fill text-danger mb-3" style="font-size: 3rem;"></i>
|
||||||
<h4 class="mb-3">¿Anular Venta #<span id="reverse-modal-id"></span>?</h4>
|
<h4 class="mb-3">¿Anular Venta #<span id="reverse-modal-id"></span>?</h4>
|
||||||
<p class="text-muted small px-3">Los productos regresarán automáticamente al inventario y el ticket será eliminado permanentemente.</p>
|
<p class="text-muted small px-3">Los productos regresarán automáticamente al inventario y el ticket
|
||||||
|
será eliminado permanentemente.</p>
|
||||||
|
|
||||||
<div class="d-flex gap-2 justify-content-center mt-4 px-3">
|
<div class="d-flex gap-2 justify-content-center mt-4 px-3">
|
||||||
<button class="btn btn-secondary w-50" data-bs-dismiss="modal">Cancelar</button>
|
<button class="btn btn-secondary w-50" data-bs-dismiss="modal">Cancelar</button>
|
||||||
@@ -230,7 +286,7 @@
|
|||||||
if (!isNaN(date)) {
|
if (!isNaN(date)) {
|
||||||
el.innerText = date.toLocaleString('es-CL', {
|
el.innerText = date.toLocaleString('es-CL', {
|
||||||
year: 'numeric', month: '2-digit', day: '2-digit',
|
year: 'numeric', month: '2-digit', day: '2-digit',
|
||||||
hour: '2-digit', minute:'2-digit'
|
hour: '2-digit', minute: '2-digit'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -312,25 +368,9 @@
|
|||||||
alert("Error de conexión con el servidor.");
|
alert("Error de conexión con el servidor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Theme Management */
|
|
||||||
function applyTheme(t) {
|
|
||||||
document.documentElement.setAttribute('data-theme', t);
|
|
||||||
const isDark = t === 'dark';
|
|
||||||
const icon = document.getElementById('theme-icon');
|
|
||||||
const label = document.getElementById('theme-label');
|
|
||||||
if (icon) icon.className = isDark ? 'bi bi-sun me-2' : 'bi bi-moon-stars me-2';
|
|
||||||
if (label) label.innerText = isDark ? 'Modo Claro' : 'Modo Oscuro';
|
|
||||||
localStorage.setItem('theme', t);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
|
||||||
applyTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
(function initTheme() {
|
|
||||||
applyTheme(localStorage.getItem('theme') || 'light');
|
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
<script src="./static/cookieStuff.js"></script>
|
||||||
|
<script src="./static/themeStuff.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user