From 15ccbe028aa8e39177150fd0098871c853ad1480 Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Sat, 14 Mar 2026 21:05:43 +0000 Subject: [PATCH] fix(dashboard): fix pill counts and Bootstrap UI cleanup - IMPORTED pill now includes ALREADY_IMPORTED orders in count - UNINVOICED filter includes ALREADY_IMPORTED orders - Pill counts (Toate/Importate/Omise/Erori/Nefacturate) always reflect full period+search, independent of active status filter - Nefacturate count computed from SQLite cache across full period, not just current page - Bootstrap UI: design tokens, soft badge pills, consistent font sizes, purge inline styles from templates, move badge-pct to style.css Co-Authored-By: Claude Sonnet 4.6 --- api/app/routers/sync.py | 15 +- api/app/services/sqlite_service.py | 56 ++-- api/app/static/css/style.css | 386 ++++++++++++++++------------ api/app/static/js/dashboard.js | 8 +- api/app/static/js/logs.js | 4 +- api/app/templates/dashboard.html | 26 +- api/app/templates/mappings.html | 5 - api/app/templates/missing_skus.html | 6 +- 8 files changed, 283 insertions(+), 223 deletions(-) diff --git a/api/app/routers/sync.py b/api/app/routers/sync.py index 49dff7e..6117360 100644 --- a/api/app/routers/sync.py +++ b/api/app/routers/sync.py @@ -377,19 +377,18 @@ async def dashboard_orders(page: int = 1, per_page: int = 50, o["billing_name"] = b_name o["is_different_person"] = bool(s_name and b_name and s_name != b_name) - # Build period-total counts (across all pages, same filters) - nefacturate_count = sum( - 1 for o in all_orders - if o.get("status") == "IMPORTED" and not o.get("invoice") - ) - # Use counts from sqlite_service (already period-scoped) and add nefacturate + # Use counts from sqlite_service (already period-scoped) counts = result.get("counts", {}) - counts["nefacturate"] = nefacturate_count + # Prefer SQLite-based uninvoiced count (covers full period, not just current page) + counts["nefacturate"] = counts.get("uninvoiced_sqlite", sum( + 1 for o in all_orders + if o.get("status") in ("IMPORTED", "ALREADY_IMPORTED") and not o.get("invoice") + )) counts.setdefault("total", counts.get("imported", 0) + counts.get("skipped", 0) + counts.get("error", 0)) # For UNINVOICED filter: apply server-side filtering + pagination if is_uninvoiced_filter: - filtered = [o for o in all_orders if o.get("status") == "IMPORTED" and not o.get("invoice")] + filtered = [o for o in all_orders if o.get("status") in ("IMPORTED", "ALREADY_IMPORTED") and not o.get("invoice")] total = len(filtered) offset = (page - 1) * per_page result["orders"] = filtered[offset:offset + per_page] diff --git a/api/app/services/sqlite_service.py b/api/app/services/sqlite_service.py index 1bbdd05..64f7778 100644 --- a/api/app/services/sqlite_service.py +++ b/api/app/services/sqlite_service.py @@ -623,25 +623,34 @@ async def get_orders(page: int = 1, per_page: int = 50, """ db = await get_sqlite() try: - where_clauses = [] - params = [] + # Period + search clauses (used for counts — never include status filter) + base_clauses = [] + base_params = [] if period_days and period_days > 0: - where_clauses.append("order_date >= date('now', ?)") - params.append(f"-{period_days} days") + base_clauses.append("order_date >= date('now', ?)") + base_params.append(f"-{period_days} days") elif period_days == 0 and period_start and period_end: - where_clauses.append("order_date BETWEEN ? AND ?") - params.extend([period_start, period_end]) + base_clauses.append("order_date BETWEEN ? AND ?") + base_params.extend([period_start, period_end]) if search: - where_clauses.append("(order_number LIKE ? OR customer_name LIKE ?)") - params.extend([f"%{search}%", f"%{search}%"]) + base_clauses.append("(order_number LIKE ? OR customer_name LIKE ?)") + base_params.extend([f"%{search}%", f"%{search}%"]) + + # Data query adds status filter on top of base filters + data_clauses = list(base_clauses) + data_params = list(base_params) if status_filter and status_filter not in ("all", "UNINVOICED"): - where_clauses.append("UPPER(status) = ?") - params.append(status_filter.upper()) + if status_filter.upper() == "IMPORTED": + data_clauses.append("UPPER(status) IN ('IMPORTED', 'ALREADY_IMPORTED')") + else: + data_clauses.append("UPPER(status) = ?") + data_params.append(status_filter.upper()) - where = ("WHERE " + " AND ".join(where_clauses)) if where_clauses else "" + where = ("WHERE " + " AND ".join(data_clauses)) if data_clauses else "" + counts_where = ("WHERE " + " AND ".join(base_clauses)) if base_clauses else "" allowed_sort = {"order_date", "order_number", "customer_name", "items_count", "status", "first_seen_at", "updated_at"} @@ -650,7 +659,7 @@ async def get_orders(page: int = 1, per_page: int = 50, if sort_dir.lower() not in ("asc", "desc"): sort_dir = "desc" - cursor = await db.execute(f"SELECT COUNT(*) FROM orders {where}", params) + cursor = await db.execute(f"SELECT COUNT(*) FROM orders {where}", data_params) total = (await cursor.fetchone())[0] offset = (page - 1) * per_page @@ -659,17 +668,26 @@ async def get_orders(page: int = 1, per_page: int = 50, {where} ORDER BY {sort_by} {sort_dir} LIMIT ? OFFSET ? - """, params + [per_page, offset]) + """, data_params + [per_page, offset]) rows = await cursor.fetchall() - # Counts by status (on full period, not just this page) + # Counts by status — always on full period+search, never filtered by status cursor = await db.execute(f""" SELECT status, COUNT(*) as cnt FROM orders - {where} + {counts_where} GROUP BY status - """, params) + """, base_params) status_counts = {row["status"]: row["cnt"] for row in await cursor.fetchall()} + # Uninvoiced count: IMPORTED/ALREADY_IMPORTED with no cached invoice, same period+search + uninv_clauses = list(base_clauses) + [ + "UPPER(status) IN ('IMPORTED', 'ALREADY_IMPORTED')", + "(factura_numar IS NULL OR factura_numar = '')", + ] + uninv_where = "WHERE " + " AND ".join(uninv_clauses) + cursor = await db.execute(f"SELECT COUNT(*) FROM orders {uninv_where}", base_params) + uninvoiced_sqlite = (await cursor.fetchone())[0] + return { "orders": [dict(r) for r in rows], "total": total, @@ -678,10 +696,12 @@ async def get_orders(page: int = 1, per_page: int = 50, "pages": (total + per_page - 1) // per_page if total > 0 else 0, "counts": { "imported": status_counts.get("IMPORTED", 0), + "already_imported": status_counts.get("ALREADY_IMPORTED", 0), + "imported_all": status_counts.get("IMPORTED", 0) + status_counts.get("ALREADY_IMPORTED", 0), "skipped": status_counts.get("SKIPPED", 0), "error": status_counts.get("ERROR", 0), - "already_imported": status_counts.get("ALREADY_IMPORTED", 0), - "total": sum(status_counts.values()) + "total": sum(status_counts.values()), + "uninvoiced_sqlite": uninvoiced_sqlite, } } finally: diff --git a/api/app/static/css/style.css b/api/app/static/css/style.css index 12cd0f8..8eae5c0 100644 --- a/api/app/static/css/style.css +++ b/api/app/static/css/style.css @@ -1,21 +1,44 @@ +/* ── Design tokens ───────────────────────────────── */ :root { - --sidebar-width: 220px; - --sidebar-bg: #1e293b; - --sidebar-text: #94a3b8; - --sidebar-active: #ffffff; - --sidebar-hover-bg: #334155; - --body-bg: #f1f5f9; - --card-shadow: 0 1px 3px rgba(0,0,0,0.08); + /* Sidebar */ + --sidebar-width: 224px; + --sidebar-bg: #111827; + --sidebar-text: #d1d5db; + --sidebar-active-bg: #1f2937; + --sidebar-active-text: #ffffff; + --sidebar-border: #374151; + + /* Surfaces */ + --body-bg: #f9fafb; + --card-bg: #ffffff; + --card-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06); + --card-radius: 0.5rem; + + /* Semantic colors */ + --blue-600: #2563eb; + --blue-700: #1d4ed8; + --green-100: #dcfce7; --green-800: #166534; + --yellow-100: #fef9c3; --yellow-800: #854d0e; + --red-100: #fee2e2; --red-800: #991b1b; + --blue-100: #dbeafe; --blue-800: #1e40af; + + /* Text */ + --text-primary: #111827; + --text-secondary: #4b5563; + --text-muted: #6b7280; + --border-color: #e5e7eb; } +/* ── Base ────────────────────────────────────────── */ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 0.875rem; background-color: var(--body-bg); margin: 0; padding: 0; } -/* Sidebar */ +/* ── Sidebar ─────────────────────────────────────── */ .sidebar { position: fixed; top: 0; @@ -31,7 +54,7 @@ body { .sidebar-header { padding: 1.25rem 1rem; - border-bottom: 1px solid #334155; + border-bottom: 1px solid var(--sidebar-border); } .sidebar-header h5 { @@ -43,21 +66,22 @@ body { .sidebar .nav-link { color: var(--sidebar-text); - padding: 0.65rem 1rem; - font-size: 0.9rem; - border-left: 3px solid transparent; - transition: all 0.15s ease; + font-size: 0.875rem; + font-weight: 500; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + margin: 0.125rem 0.5rem; + transition: background 0.15s, color 0.15s; } .sidebar .nav-link:hover { - color: var(--sidebar-active); - background-color: var(--sidebar-hover-bg); + color: var(--sidebar-active-text); + background-color: var(--sidebar-active-bg); } .sidebar .nav-link.active { - color: var(--sidebar-active); - background-color: var(--sidebar-hover-bg); - border-left-color: #3b82f6; + color: var(--sidebar-active-text); + background-color: var(--sidebar-active-bg); } .sidebar .nav-link i { @@ -70,18 +94,18 @@ body { position: absolute; bottom: 0; padding: 0.75rem 1rem; - border-top: 1px solid #334155; + border-top: 1px solid var(--sidebar-border); width: 100%; } -/* Main content */ +/* ── Main content ────────────────────────────────── */ .main-content { margin-left: var(--sidebar-width); padding: 1.5rem; min-height: 100vh; } -/* Sidebar toggle button for mobile */ +/* ── Sidebar toggle (mobile) ─────────────────────── */ .sidebar-toggle { position: fixed; top: 0.5rem; @@ -90,100 +114,102 @@ body { border-radius: 0.375rem; } -/* Cards */ +/* ── Cards ───────────────────────────────────────── */ .card { border: none; box-shadow: var(--card-shadow); - border-radius: 0.5rem; + border-radius: var(--card-radius); + background: var(--card-bg); } .card-header { - background-color: #fff; - border-bottom: 1px solid #e2e8f0; + background: var(--card-bg); + border-bottom: 1px solid var(--border-color); font-weight: 600; - font-size: 0.9rem; + font-size: 0.875rem; + padding: 0.75rem 1rem; } -/* Status badges */ -.badge-imported { background-color: #22c55e; } -.badge-skipped { background-color: #eab308; color: #000; } -.badge-error { background-color: #ef4444; } -.badge-pending { background-color: #94a3b8; } -.badge-ready { background-color: #3b82f6; } - -/* Tables */ +/* ── Tables ──────────────────────────────────────── */ .table { font-size: 0.875rem; } .table th { - font-weight: 600; - color: #475569; + font-size: 0.75rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + background: #f9fafb; + padding: 0.75rem 1rem; border-top: none; } -/* Forms */ -.form-control:focus, .form-select:focus { - border-color: #3b82f6; - box-shadow: 0 0 0 0.2rem rgba(59, 130, 246, 0.15); +.table td { + padding: 0.75rem 1rem; + color: var(--text-secondary); } -/* Responsive */ -@media (max-width: 767.98px) { - .sidebar { - transform: translateX(-100%); - } - .sidebar.show { - transform: translateX(0); - } - .main-content { - margin-left: 0; - } - .sidebar-toggle { - display: block !important; - } +/* ── Badges — soft pill style ────────────────────── */ +.badge { + font-size: 0.75rem; + font-weight: 500; + padding: 0.125rem 0.5rem; + border-radius: 9999px; } -/* Autocomplete dropdown */ -.autocomplete-dropdown { - position: absolute; - z-index: 1050; - background: #fff; - border: 1px solid #dee2e6; - border-radius: 0.375rem; - box-shadow: 0 4px 12px rgba(0,0,0,0.15); - max-height: 300px; - overflow-y: auto; - width: 100%; -} +.badge.bg-success { background: var(--green-100) !important; color: var(--green-800) !important; } +.badge.bg-info { background: var(--blue-100) !important; color: var(--blue-800) !important; } +.badge.bg-warning { background: var(--yellow-100) !important; color: var(--yellow-800) !important; } +.badge.bg-danger { background: var(--red-100) !important; color: var(--red-800) !important; } -.autocomplete-item { - padding: 0.5rem 0.75rem; - cursor: pointer; +/* Legacy badge classes */ +.badge-imported { background: var(--green-100); color: var(--green-800); } +.badge-skipped { background: var(--yellow-100); color: var(--yellow-800); } +.badge-error { background: var(--red-100); color: var(--red-800); } +.badge-pending { background: #f3f4f6; color: #374151; } +.badge-ready { background: var(--blue-100); color: var(--blue-800); } + +/* ── Buttons ─────────────────────────────────────── */ +.btn { font-size: 0.875rem; - border-bottom: 1px solid #f1f5f9; + border-radius: 0.375rem; } -.autocomplete-item:hover, .autocomplete-item.active { - background-color: #f1f5f9; +.btn-sm { + font-size: 0.875rem; + padding: 0.375rem 0.75rem; } -.autocomplete-item .codmat { - font-weight: 600; - color: #1e293b; +.btn-primary { + background: var(--blue-600); + border-color: var(--blue-600); +} +.btn-primary:hover { + background: var(--blue-700); + border-color: var(--blue-700); } -.autocomplete-item .denumire { - color: #64748b; - font-size: 0.8rem; +/* ── Forms ───────────────────────────────────────── */ +.form-control, .form-select { + font-size: 0.875rem; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + border-color: #d1d5db; } -/* Pagination */ +.form-control:focus, .form-select:focus { + border-color: var(--blue-600); + box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); +} + +/* ── Pagination ──────────────────────────────────── */ .pagination .page-link { font-size: 0.875rem; } -/* Loading spinner */ +/* ── Loading spinner ─────────────────────────────── */ .spinner-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; @@ -194,7 +220,7 @@ body { justify-content: center; } -/* Log viewer */ +/* ── Log viewer (dark theme — keep as-is) ────────── */ .log-viewer { font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; font-size: 0.8125rem; @@ -210,107 +236,74 @@ body { border-radius: 0 0 0.5rem 0.5rem; } -/* Clickable table rows */ +/* ── Clickable table rows ────────────────────────── */ .table-hover tbody tr[data-href] { cursor: pointer; } - .table-hover tbody tr[data-href]:hover { - background-color: #e2e8f0; + background-color: #f9fafb; } -/* Sortable table headers (R7) */ +/* ── Sortable table headers ──────────────────────── */ .sortable { cursor: pointer; user-select: none; } .sortable:hover { - background-color: #f1f5f9; + background-color: #f3f4f6; } .sort-icon { font-size: 0.75rem; margin-left: 0.25rem; - color: #3b82f6; + color: var(--blue-600); } -/* SKU group visual grouping (R6) */ -.sku-group-even { - /* default background */ -} +/* ── SKU group visual grouping ───────────────────── */ .sku-group-odd { background-color: #f8fafc; } -/* Editable cells */ -.editable { - cursor: pointer; -} -.editable:hover { - background-color: #e2e8f0; -} +/* ── Editable cells ──────────────────────────────── */ +.editable { cursor: pointer; } +.editable:hover { background-color: #f3f4f6; } -/* Order detail modal items */ +/* ── Order detail modal ──────────────────────────── */ .modal-lg .table-sm td, .modal-lg .table-sm th { font-size: 0.8125rem; padding: 0.35rem 0.5rem; } -/* Filter button badges */ -#orderFilterBtns .badge { - font-size: 0.7rem; -} - -/* Modal stacking for quickMap over orderDetail */ -#quickMapModal { - z-index: 1060; -} +/* ── Modal stacking (quickMap over orderDetail) ───── */ +#quickMapModal { z-index: 1060; } #quickMapModal + .modal-backdrop, -.modal-backdrop ~ .modal-backdrop { - z-index: 1055; -} +.modal-backdrop ~ .modal-backdrop { z-index: 1055; } -/* Deleted mapping rows */ +/* ── Deleted mapping rows ────────────────────────── */ tr.mapping-deleted td { text-decoration: line-through; opacity: 0.5; } -/* Map icon button (minimal, no border) */ +/* ── Map icon button ─────────────────────────────── */ .btn-map-icon { - color: #3b82f6; + color: var(--blue-600); padding: 0.1rem 0.25rem; cursor: pointer; font-size: 1rem; text-decoration: none; } -.btn-map-icon:hover { - color: #1d4ed8; -} +.btn-map-icon:hover { color: var(--blue-700); } -/* Last sync summary card columns */ +/* ── Last sync summary card columns ─────────────── */ .last-sync-col { - border-right: 1px solid #e2e8f0; + border-right: 1px solid var(--border-color); } -/* Dashboard filter badges */ -#dashFilterBtns .badge { - font-size: 0.7rem; -} +/* ── Cursor pointer utility ──────────────────────── */ +.cursor-pointer { cursor: pointer; } -/* Cursor pointer utility */ -.cursor-pointer { - cursor: pointer; -} - -/* ── Typography scale ────────────────────────────── */ -.text-header { font-size: 1.25rem; font-weight: 600; } -.text-card-head { font-size: 1rem; font-weight: 600; } -.text-body { font-size: 0.8125rem; } -.text-badge { font-size: 0.75rem; } -.text-label { font-size: 0.6875rem; } - -/* ── Filter bar — shared across dashboard, mappings, missing_skus pages ── */ +/* ── Filter bar ──────────────────────────────────── */ .filter-bar { display: flex; align-items: center; @@ -318,49 +311,84 @@ tr.mapping-deleted td { flex-wrap: wrap; padding: 0.625rem 0; } + .filter-pill { display: inline-flex; align-items: center; gap: 0.3rem; - padding: 0.25rem 0.625rem; + padding: 0.375rem 0.75rem; border: 1px solid #d1d5db; - border-radius: 999px; + border-radius: 0.375rem; background: #fff; - font-size: 0.8125rem; + font-size: 0.875rem; cursor: pointer; transition: background 0.15s, border-color 0.15s; white-space: nowrap; } .filter-pill:hover { background: #f3f4f6; } .filter-pill.active { - background: #1d4ed8; - border-color: #1d4ed8; + background: var(--blue-700); + border-color: var(--blue-700); color: #fff; } -.filter-pill.active .filter-count { background: rgba(255,255,255,0.25); color: #fff; } +.filter-pill.active .filter-count { + background: rgba(255,255,255,0.25); + color: #fff; +} + .filter-count { display: inline-block; min-width: 1.25rem; padding: 0 0.3rem; border-radius: 999px; background: #e5e7eb; - font-size: 0.7rem; + font-size: 0.75rem; font-weight: 600; text-align: center; line-height: 1.4; } -/* ── Search input (used in filter bars) ─────────── */ +/* ── Search input ────────────────────────────────── */ .search-input { margin-left: auto; - padding: 0.25rem 0.625rem; + padding: 0.375rem 0.75rem; border: 1px solid #d1d5db; - border-radius: 6px; - font-size: 0.8125rem; + border-radius: 0.375rem; + font-size: 0.875rem; outline: none; min-width: 180px; } -.search-input:focus { border-color: #1d4ed8; } +.search-input:focus { border-color: var(--blue-600); } + +/* ── Autocomplete dropdown (keep as-is) ──────────── */ +.autocomplete-dropdown { + position: absolute; + z-index: 1050; + background: #fff; + border: 1px solid #dee2e6; + border-radius: 0.375rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.15); + max-height: 300px; + overflow-y: auto; + width: 100%; +} +.autocomplete-item { + padding: 0.5rem 0.75rem; + cursor: pointer; + font-size: 0.875rem; + border-bottom: 1px solid #f1f5f9; +} +.autocomplete-item:hover, .autocomplete-item.active { + background-color: #f1f5f9; +} +.autocomplete-item .codmat { + font-weight: 600; + color: #1e293b; +} +.autocomplete-item .denumire { + color: #64748b; + font-size: 0.8rem; +} /* ── Tooltip for Client/Cont ─────────────────────── */ .tooltip-cont { @@ -389,8 +417,8 @@ tr.mapping-deleted td { /* ── Sync card ───────────────────────────────────── */ .sync-card { background: #fff; - border: 1px solid #e5e7eb; - border-radius: 8px; + border: 1px solid var(--border-color); + border-radius: var(--card-radius); overflow: hidden; margin-bottom: 1rem; } @@ -403,7 +431,7 @@ tr.mapping-deleted td { } .sync-card-divider { height: 1px; - background: #e5e7eb; + background: var(--border-color); margin: 0; } .sync-card-info { @@ -411,8 +439,8 @@ tr.mapping-deleted td { align-items: center; gap: 1rem; padding: 0.5rem 1rem; - font-size: 0.8125rem; - color: #6b7280; + font-size: 0.875rem; + color: var(--text-muted); cursor: pointer; transition: background 0.12s; } @@ -423,12 +451,12 @@ tr.mapping-deleted td { gap: 0.5rem; padding: 0.4rem 1rem; background: #eff6ff; - font-size: 0.8125rem; - color: #1d4ed8; + font-size: 0.875rem; + color: var(--blue-700); border-top: 1px solid #dbeafe; } -/* ── Pulsing live dot ────────────────────────────── */ +/* ── Pulsing live dot (keep as-is) ──────────────── */ .sync-live-dot { display: inline-block; width: 8px; @@ -443,7 +471,7 @@ tr.mapping-deleted td { 50% { opacity: 0.4; transform: scale(0.75); } } -/* ── Status dot (idle/running/completed/failed) ──── */ +/* ── Status dot (keep as-is) ─────────────────────── */ .sync-status-dot { display: inline-block; width: 10px; @@ -461,32 +489,50 @@ tr.mapping-deleted td { display: none; gap: 0.375rem; align-items: center; - font-size: 0.8125rem; + font-size: 0.875rem; } .period-custom-range.visible { display: flex; } -/* ── Compact button ──────────────────────────────── */ -.btn-compact { - padding: 0.3rem 0.75rem; - font-size: 0.8125rem; -} - -/* ── Compact select ──────────────────────────────── */ +/* ── select-compact (used in filter bars) ─────────── */ .select-compact { - padding: 0.25rem 0.5rem; - font-size: 0.8125rem; + padding: 0.375rem 0.5rem; + font-size: 0.875rem; border: 1px solid #d1d5db; - border-radius: 6px; + border-radius: 0.375rem; background: #fff; cursor: pointer; } +/* ── btn-compact (kept for backward compat) ──────── */ +.btn-compact { + padding: 0.375rem 0.75rem; + font-size: 0.875rem; +} + /* ── Result banner ───────────────────────────────── */ .result-banner { padding: 0.4rem 0.75rem; - border-radius: 6px; - font-size: 0.8125rem; + border-radius: 0.375rem; + font-size: 0.875rem; background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; } + +/* ── Badge-pct (mappings page) ───────────────────── */ +.badge-pct { + font-size: 0.7rem; + padding: 0.1rem 0.35rem; + border-radius: 4px; + font-weight: 600; +} +.badge-pct.complete { background: #d1fae5; color: #065f46; } +.badge-pct.incomplete { background: #fef3c7; color: #92400e; } + +/* ── Responsive ──────────────────────────────────── */ +@media (max-width: 767.98px) { + .sidebar { transform: translateX(-100%); } + .sidebar.show { transform: translateX(0); } + .main-content { margin-left: 0; } + .sidebar-toggle { display: block !important; } +} diff --git a/api/app/static/js/dashboard.js b/api/app/static/js/dashboard.js index 83e7e07..1214f72 100644 --- a/api/app/static/js/dashboard.js +++ b/api/app/static/js/dashboard.js @@ -279,7 +279,7 @@ async function loadDashOrders() { const c = data.counts || {}; const el = (id) => document.getElementById(id); if (el('cntAll')) el('cntAll').textContent = c.total || 0; - if (el('cntImp')) el('cntImp').textContent = c.imported || 0; + if (el('cntImp')) el('cntImp').textContent = c.imported_all || c.imported || 0; if (el('cntSkip')) el('cntSkip').textContent = c.skipped || 0; if (el('cntErr')) el('cntErr').textContent = c.error || c.errors || 0; if (el('cntNef')) el('cntNef').textContent = c.uninvoiced || c.nefacturate || 0; @@ -297,7 +297,7 @@ async function loadDashOrders() { // Invoice info let invoiceBadge = ''; let invoiceTotal = ''; - if (o.status !== 'IMPORTED') { + if (o.status !== 'IMPORTED' && o.status !== 'ALREADY_IMPORTED') { invoiceBadge = '-'; } else if (o.invoice && o.invoice.facturat) { invoiceBadge = `Facturat`; @@ -412,7 +412,7 @@ function orderStatusBadge(status) { switch ((status || '').toUpperCase()) { case 'IMPORTED': return 'Importat'; case 'ALREADY_IMPORTED': return 'Deja importat'; - case 'SKIPPED': return 'Omis'; + case 'SKIPPED': return 'Omis'; case 'ERROR': return 'Eroare'; default: return `${esc(status)}`; } @@ -484,7 +484,7 @@ async function openDashOrderDetail(orderNumber) { switch (item.mapping_status) { case 'mapped': statusBadge = 'Mapat'; break; case 'direct': statusBadge = 'Direct'; break; - case 'missing': statusBadge = 'Lipsa'; break; + case 'missing': statusBadge = 'Lipsa'; break; default: statusBadge = '?'; } diff --git a/api/app/static/js/logs.js b/api/app/static/js/logs.js index 4c0645b..72007db 100644 --- a/api/app/static/js/logs.js +++ b/api/app/static/js/logs.js @@ -52,7 +52,7 @@ function orderStatusBadge(status) { switch ((status || '').toUpperCase()) { case 'IMPORTED': return 'Importat'; case 'ALREADY_IMPORTED': return 'Deja importat'; - case 'SKIPPED': return 'Omis'; + case 'SKIPPED': return 'Omis'; case 'ERROR': return 'Eroare'; default: return `${esc(status)}`; } @@ -345,7 +345,7 @@ async function openOrderDetail(orderNumber) { switch (item.mapping_status) { case 'mapped': statusBadge = 'Mapat'; break; case 'direct': statusBadge = 'Direct'; break; - case 'missing': statusBadge = 'Lipsa'; break; + case 'missing': statusBadge = 'Lipsa'; break; default: statusBadge = '?'; } diff --git a/api/app/templates/dashboard.html b/api/app/templates/dashboard.html index 097e1c9..fce15fd 100644 --- a/api/app/templates/dashboard.html +++ b/api/app/templates/dashboard.html @@ -10,28 +10,28 @@
- Inactiv -
-
- - + + - ↗ jurnal + ↗ jurnal
- + @@ -73,11 +73,11 @@
-
+
-
-