GDPR suglasnost 2026 β obvezno do 2026-06-01
diff --git a/static/crm.html b/static/crm.html
index 612574c..7303ce6 100644
--- a/static/crm.html
+++ b/static/crm.html
@@ -136,7 +136,8 @@ table tr:hover td { background: rgba(26, 115, 232, 0.05); }
.toast.err { border-left-color: var(--err); }
-
+
+
@@ -158,6 +159,7 @@ table tr:hover td { background: rgba(26, 115, 232, 0.05); }
π Obrasci β¦
π Statistika
π Notifikacije β¦
+
π¨ E-mail templates
ROLA:
@@ -258,6 +261,7 @@ function setTab(name) {
if (name === 'obrasci') loadObrasci();
if (name === 'stats') loadStats();
if (name === 'notifs') loadNotifs();
+ if (name === 'emailtpl') loadEmailTpl();
}
// ββββββββββββββββββββββββββββββββββββββββββββββββββββ
@@ -294,7 +298,8 @@ async function loadClanarine() {
-
+
+
@@ -381,6 +386,33 @@ async function doBulkNotify(body) {
} catch (e) { toast('GreΕ‘ka: ' + e.message, true); }
}
+async function bulkUplatniceZipSelected() {
+ const sel = getSelectedClanarine();
+ const body = sel.length ? {ids: sel.map(s => s.id), only_unpaid: false} : {};
+ if (!sel.length && !confirm('NiΕ‘ta nije odabrano β generirati ZIP za SVE duΕΎnike?')) return;
+ toast(`Generiranje ZIP-a (${sel.length || 'svi'})... moΕΎe potrajati`);
+ try {
+ const r = await fetch(API + '/clanarine/bulk/uplatnice.zip', {
+ method: 'POST',
+ headers: {'Content-Type':'application/json'},
+ body: JSON.stringify(body),
+ });
+ if (!r.ok) {
+ const t = await r.text();
+ throw new Error(`HTTP ${r.status}: ${t.substring(0,200)}`);
+ }
+ const blob = await r.blob();
+ const cnt = r.headers.get('X-Batch-Count') || '?';
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `hub3-batch-${new Date().toISOString().slice(0,10)}-${cnt}.zip`;
+ document.body.appendChild(a); a.click(); a.remove();
+ URL.revokeObjectURL(url);
+ toast(`β ZIP preuzeto (${cnt} PDF-ova, ${(blob.size/1024).toFixed(0)} KB)`);
+ } catch (e) { toast('GreΕ‘ka: ' + e.message, true); }
+}
+
async function bulkUplatniceSelected() {
const sel = getSelectedClanarine();
const body = sel.length ? {ids: sel.map(s => s.id)} : {};
diff --git a/static/erp.html b/static/erp.html
index 74b5adb..b6f0f60 100644
--- a/static/erp.html
+++ b/static/erp.html
@@ -19,8 +19,9 @@
}
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Inter',system-ui,sans-serif; background:var(--bg); color:var(--text); min-height:100vh; font-size:14px; }
-.app { display:grid; grid-template-columns:230px 1fr; min-height:100vh; }
-.sidebar { background:var(--bg-2); border-right:1px solid var(--border); padding:20px 0; }
+.app { display:grid; grid-template-columns:1fr; min-height:100vh; }
+/* Native sidebar hidden β shared sidebar (/static/shared/sidebar.*) handles sectioned menu */
+.sidebar { display:none; }
.brand { padding:0 20px 18px; border-bottom:1px solid var(--border); margin-bottom:10px; }
.brand h1 { font-size:16px; font-weight:700; color:var(--accent); font-family:'JetBrains Mono',monospace; }
.brand .sub { font-size:11px; color:var(--text-3); margin-top:2px; }
@@ -79,6 +80,8 @@ tr.clickable:hover { background:var(--bg-3); box-shadow:inset 3px 0 0 var(--acce
.actions-row { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; padding-top:14px; border-top:1px solid var(--border); }
@media(max-width:768px) { .app { grid-template-columns:1fr; } .sidebar { display:none; } .grid2,.grid3 { grid-template-columns:1fr; } .col2 { grid-template-columns:1fr; } .audit-row { grid-template-columns:1fr; } }
+
+
@@ -91,13 +94,13 @@ tr.clickable:hover { background:var(--bg-3); box-shadow:inset 3px 0 0 var(--acce
πLista putnih naloga
Portali
-
πPrijava
-
π±Aplikacija
-
π‘Administracija
-
π₯CRM
-
π°ERP
-
πKPI
-
πAudit
+
πPrijava
+
π±Aplikacija
+
π‘Administracija
+
π₯CRM
+
π°ERP
+
πKPI
+
πAudit
πPublic portal
diff --git a/static/shared/sidebar.css b/static/shared/sidebar.css
index 0ed0118..e9fdf2e 100644
--- a/static/shared/sidebar.css
+++ b/static/shared/sidebar.css
@@ -1,7 +1,7 @@
-/* PGΕ½ SPORT β Unified Sidebar v1.0
+/* PGΕ½ SPORT β Unified Sectioned Sidebar v2.0
* dradulic@outlook.com / damir@rinet.one β 2026-05-05
+ * Reference: app.rinet.one/klasik/dabi
* Used by: sport2.html, app.html, admin.html, crm.html, erp.html, audit.html, kpi.html, login.html
- * Reference: app.rinet.one/klasik/control
*/
:root{
@@ -10,7 +10,7 @@
--rim:#1e2a50; --rim2:#283560;
--t0:#fff; --t1:#e2e6f0; --t2:#8a95b4; --t4:#4e5a7a;
--green:#00e88f; --red:#ff2d55; --amber:#f59e0b; --cyan:#00c8e8;
- --sb-w-exp:230px; --sb-w-col:58px;
+ --sb-w-exp:240px; --sb-w-col:58px;
}
#pgz-sb{
@@ -18,74 +18,108 @@
background:linear-gradient(180deg,var(--bg1) 0%,var(--bg0) 100%);
border-right:1px solid var(--rim);
display:flex; flex-direction:column; z-index:100;
- font-family:'Inter',sans-serif; font-size:13px; color:var(--t1);
+ font-family:'Inter','Segoe UI',sans-serif; font-size:13px; color:var(--t1);
transition:width .22s ease, transform .22s ease;
}
#pgz-sb *{box-sizing:border-box}
#pgz-sb a{text-decoration:none;color:inherit}
/* Header */
-.pgz-sb-h{padding:18px 18px 14px;border-bottom:1px solid var(--rim);position:relative;flex-shrink:0}
-.pgz-sb-h .pgz-logo{font-weight:800;font-size:14px;color:var(--t0);letter-spacing:.5px;white-space:nowrap;overflow:hidden}
+.pgz-sb-h{padding:14px 16px 12px;border-bottom:1px solid var(--rim);position:relative;flex-shrink:0;display:flex;align-items:center;gap:10px}
+.pgz-sb-h .pgz-mark{
+ width:28px;height:28px;border-radius:6px;flex-shrink:0;
+ background:linear-gradient(135deg,var(--pgz-blue) 0%,var(--pgz-blue2) 100%);
+ color:#fff;font-weight:800;font-size:13px;
+ display:flex;align-items:center;justify-content:center;
+ border:1px solid var(--pgz-gold);
+}
+.pgz-sb-h .pgz-htxt{flex:1;min-width:0;overflow:hidden}
+.pgz-sb-h .pgz-logo{font-weight:800;font-size:12.5px;color:var(--t0);letter-spacing:.3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pgz-sb-h .pgz-logo .g{color:var(--pgz-gold)}
-.pgz-sb-h .pgz-sub{font-size:10px;color:var(--t2);margin-top:4px;text-transform:uppercase;letter-spacing:1px;white-space:nowrap;overflow:hidden}
+.pgz-sb-h .pgz-sub{font-size:9.5px;color:var(--t2);margin-top:2px;text-transform:uppercase;letter-spacing:.6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pgz-sb-toggle{
- position:absolute;top:14px;right:8px;width:24px;height:24px;
+ width:24px;height:24px;flex-shrink:0;
display:flex;align-items:center;justify-content:center;cursor:pointer;
color:var(--t2);background:var(--bg2);border:1px solid var(--rim);
- border-radius:5px;font-size:14px;font-weight:700;
+ border-radius:5px;font-size:13px;font-weight:700;
transition:all .15s;user-select:none;
}
.pgz-sb-toggle:hover{background:var(--bg3);color:var(--pgz-gold);border-color:var(--pgz-gold)}
-/* Section label / separator */
-.pgz-sb-sep{padding:14px 14px 4px 14px;font-size:9.5px;color:var(--t4);
- text-transform:uppercase;letter-spacing:1.2px;font-weight:700;
- white-space:nowrap;overflow:hidden}
+/* Section header */
+.pgz-sb-section{padding:14px 16px 4px;font-size:9.5px;color:var(--t4);
+ text-transform:uppercase;letter-spacing:1.4px;font-weight:700;
+ white-space:nowrap;overflow:hidden;
+ display:flex;align-items:center;gap:6px;
+}
+.pgz-sb-section::after{content:"";flex:1;height:1px;background:var(--rim);opacity:.7}
+.pgz-sb-section.pgz-admin{color:var(--pgz-gold)}
/* Nav */
-.pgz-sb-nav{flex:1;padding:6px 8px;overflow-y:auto;overflow-x:hidden}
+.pgz-sb-nav{flex:1;padding:4px 8px 8px;overflow-y:auto;overflow-x:hidden}
.pgz-sb-nav::-webkit-scrollbar{width:6px}
.pgz-sb-nav::-webkit-scrollbar-thumb{background:var(--rim2);border-radius:3px}
.pgz-nav-i{
- padding:9px 12px;border-radius:6px;color:var(--t2);
+ padding:7px 12px;border-radius:5px;color:var(--t2);
cursor:pointer;display:flex;align-items:center;gap:10px;
- font-size:12.5px;margin-bottom:2px;white-space:nowrap;
- transition:background .15s,color .15s;position:relative;
+ font-size:12.5px;margin:1px 0;white-space:nowrap;
+ transition:background .12s,color .12s;position:relative;
+ border-left:2px solid transparent;
}
-.pgz-nav-i:hover{background:var(--bg2);color:var(--t1)}
+.pgz-nav-i:hover{background:var(--bg2);color:var(--t1);border-left-color:var(--pgz-blue2)}
.pgz-nav-i.active{
- background:linear-gradient(90deg,var(--pgz-blue) 0%,var(--pgz-blue2) 100%);
- color:#fff;font-weight:600;
+ background:linear-gradient(90deg,rgba(0,76,196,.18) 0%,transparent 80%);
+ color:#fff;font-weight:600;border-left-color:var(--pgz-gold);
}
-.pgz-nav-i .ic{width:20px;text-align:center;font-size:14px;flex-shrink:0}
+.pgz-nav-i.active .ic{color:var(--pgz-gold)}
+.pgz-nav-i .ic{width:20px;text-align:center;font-size:13px;flex-shrink:0;color:var(--t2);transition:color .12s}
+.pgz-nav-i:hover .ic{color:var(--pgz-gold)}
.pgz-nav-i .lbl{overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}
.pgz-nav-i .badge{margin-left:auto;background:var(--red);color:#fff;font-size:9px;font-weight:700;padding:1px 6px;border-radius:8px;flex-shrink:0}
-.pgz-nav-ext{color:var(--cyan)}
-.pgz-nav-ext::after{content:"β";font-size:10px;opacity:.5;margin-left:auto;flex-shrink:0}
-.pgz-nav-ext:hover{color:var(--pgz-gold);background:var(--bg2)}
-.pgz-nav-ext.active{background:linear-gradient(90deg,var(--pgz-blue) 0%,var(--pgz-blue2) 100%);color:#fff}
-.pgz-nav-ext.active::after{opacity:.85}
+.pgz-nav-i.cross-portal::after{content:"β";font-size:9px;opacity:.4;margin-left:6px;flex-shrink:0}
+.pgz-nav-i.cross-portal:hover::after{opacity:.85}
/* Footer (user) */
-.pgz-sb-foot{padding:10px 12px;border-top:1px solid var(--rim);
+.pgz-sb-foot{padding:8px 10px;border-top:1px solid var(--rim);
display:flex;align-items:center;gap:8px;
- white-space:nowrap;overflow:hidden;flex-shrink:0}
+ white-space:nowrap;overflow:hidden;flex-shrink:0;
+ cursor:pointer;transition:background .15s;position:relative;
+}
+.pgz-sb-foot:hover{background:var(--bg2)}
.pgz-sb-foot .av{
- width:30px;height:30px;border-radius:50%;
+ width:32px;height:32px;border-radius:50%;
background:linear-gradient(135deg,var(--pgz-blue),var(--pgz-gold));
color:#fff;font-weight:800;display:flex;align-items:center;justify-content:center;
font-size:11px;flex-shrink:0;overflow:hidden;
+ border:2px solid transparent;transition:border-color .15s;
}
+.pgz-sb-foot:hover .av{border-color:var(--pgz-gold)}
.pgz-sb-foot .av img{width:100%;height:100%;object-fit:cover}
.pgz-sb-foot .ui{flex:1;min-width:0;overflow:hidden}
.pgz-sb-foot .un{font-size:11.5px;color:var(--t1);font-weight:600;line-height:1.2;overflow:hidden;text-overflow:ellipsis}
.pgz-sb-foot .ur{font-size:9.5px;color:var(--t4);text-transform:uppercase;letter-spacing:.5px;line-height:1.2;overflow:hidden;text-overflow:ellipsis}
-.pgz-sb-foot .lo{cursor:pointer;color:var(--t4);font-size:14px;
- padding:6px 8px;border-radius:5px;transition:all .15s;flex-shrink:0}
-.pgz-sb-foot .lo:hover{background:rgba(255,45,85,.15);color:var(--red)}
+.pgz-sb-foot .caret{font-size:11px;color:var(--t4);transition:transform .15s}
+.pgz-sb-foot.menu-open .caret{transform:rotate(180deg)}
-/* Mobile burger (shown <768px when sidebar is offscreen) */
+/* User menu (popup above footer) */
+.pgz-user-menu{
+ position:absolute;left:8px;right:8px;bottom:54px;
+ background:var(--bg2);border:1px solid var(--rim);border-radius:6px;
+ box-shadow:0 -4px 18px rgba(0,0,0,.55);
+ padding:4px;z-index:300;display:none;
+}
+.pgz-user-menu.open{display:block}
+.pgz-user-menu a{
+ display:flex;align-items:center;gap:8px;
+ padding:8px 10px;border-radius:5px;
+ color:var(--t1);font-size:12px;cursor:pointer;
+}
+.pgz-user-menu a:hover{background:var(--bg3);color:var(--pgz-gold)}
+.pgz-user-menu .sep{height:1px;background:var(--rim);margin:3px 0}
+.pgz-user-menu .danger{color:var(--red)}
+.pgz-user-menu .danger:hover{background:rgba(255,45,85,.12)}
+
+/* Mobile burger */
.pgz-sb-burger{
position:fixed;top:10px;left:10px;z-index:99;
width:36px;height:36px;display:none;align-items:center;justify-content:center;
@@ -94,54 +128,59 @@
}
.pgz-sb-burger:hover{background:var(--bg3);color:var(--pgz-gold)}
-/* Mobile X (shown <768px when sidebar is open) */
-.pgz-sb-mx{display:none;cursor:pointer;color:var(--t2);font-size:18px;
+/* Mobile X */
+.pgz-sb-mx{display:none;cursor:pointer;color:var(--t2);font-size:16px;
width:24px;height:24px;align-items:center;justify-content:center;
- border-radius:5px;transition:all .15s}
+ border-radius:5px;transition:all .15s;flex-shrink:0}
.pgz-sb-mx:hover{background:var(--bg3);color:var(--red)}
/* βββ Collapsed state βββ */
#pgz-sb.pgz-collapsed{width:var(--sb-w-col)}
-#pgz-sb.pgz-collapsed .pgz-sb-h{padding:18px 6px 14px;text-align:center}
-#pgz-sb.pgz-collapsed .pgz-sb-h .pgz-logo{font-size:0}
-#pgz-sb.pgz-collapsed .pgz-sb-h .pgz-logo::before{content:"PG";font-size:13px;color:var(--pgz-gold);font-weight:800}
-#pgz-sb.pgz-collapsed .pgz-sb-h .pgz-sub{display:none}
-#pgz-sb.pgz-collapsed .pgz-sb-toggle{position:static;margin:6px auto 0;display:flex}
-#pgz-sb.pgz-collapsed .pgz-sb-sep{font-size:0;padding:6px 0;text-align:center;border-top:1px dashed var(--rim);margin:6px 8px 4px}
-#pgz-sb.pgz-collapsed .pgz-nav-i{justify-content:center;padding:10px 6px}
+#pgz-sb.pgz-collapsed .pgz-sb-h{padding:14px 6px 12px;justify-content:center;flex-direction:column;gap:8px}
+#pgz-sb.pgz-collapsed .pgz-sb-h .pgz-htxt{display:none}
+#pgz-sb.pgz-collapsed .pgz-sb-section{
+ font-size:0;padding:6px 0;text-align:center;
+ border-top:1px dashed var(--rim);margin:6px 8px 4px;
+}
+#pgz-sb.pgz-collapsed .pgz-sb-section::after{display:none}
+#pgz-sb.pgz-collapsed .pgz-nav-i{justify-content:center;padding:9px 6px;border-left:none}
+#pgz-sb.pgz-collapsed .pgz-nav-i.active{
+ background:linear-gradient(135deg,var(--pgz-blue),var(--pgz-blue2));
+ border-left:none;
+}
#pgz-sb.pgz-collapsed .pgz-nav-i .lbl,
#pgz-sb.pgz-collapsed .pgz-nav-i .badge,
-#pgz-sb.pgz-collapsed .pgz-nav-ext::after{display:none}
-#pgz-sb.pgz-collapsed .pgz-sb-foot{padding:10px 6px;justify-content:center}
+#pgz-sb.pgz-collapsed .pgz-nav-i.cross-portal::after{display:none}
+#pgz-sb.pgz-collapsed .pgz-sb-foot{padding:8px 6px;justify-content:center}
#pgz-sb.pgz-collapsed .pgz-sb-foot .ui,
-#pgz-sb.pgz-collapsed .pgz-sb-foot .lo{display:none}
+#pgz-sb.pgz-collapsed .pgz-sb-foot .caret{display:none}
+#pgz-sb.pgz-collapsed .pgz-user-menu{left:62px;right:auto;width:200px;bottom:8px}
/* Tooltip when collapsed */
-#pgz-sb.pgz-collapsed .pgz-nav-i:hover::after{
+#pgz-sb.pgz-collapsed .pgz-nav-i:hover::before{
content:attr(data-label);
position:absolute;left:calc(var(--sb-w-col) - 4px);top:50%;transform:translateY(-50%);
background:var(--bg3);color:var(--t0);
padding:5px 10px;border-radius:4px;
font-size:11.5px;white-space:nowrap;
border:1px solid var(--rim);font-weight:600;
- box-shadow:2px 2px 10px rgba(0,0,0,.45);
+ box-shadow:2px 2px 10px rgba(0,0,0,.55);
pointer-events:none;z-index:200;
}
-/* Layout helper β apply on body to push content right of sidebar */
+/* Layout helper: pages opt in by adding body.pgz-has-sb */
body.pgz-has-sb{padding-left:var(--sb-w-exp);transition:padding-left .22s ease}
body.pgz-has-sb.pgz-sb-col{padding-left:var(--sb-w-col)}
-/* Mobile: <768px */
+/* Mobile <768px */
@media (max-width:768px){
#pgz-sb{transform:translateX(-100%)}
#pgz-sb.pgz-mobile-open{transform:translateX(0)}
- #pgz-sb.pgz-collapsed{width:var(--sb-w-exp)} /* full width on mobile when open */
+ #pgz-sb.pgz-collapsed{width:var(--sb-w-exp)}
body.pgz-has-sb,body.pgz-has-sb.pgz-sb-col{padding-left:0}
.pgz-sb-burger{display:flex}
.pgz-sb-mx{display:flex}
.pgz-sb-toggle{display:none}
- /* overlay backdrop */
body.pgz-mobile-sb-open::before{
content:"";position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:99;backdrop-filter:blur(2px)
}
diff --git a/static/shared/sidebar.js b/static/shared/sidebar.js
index c1549dd..9a8d9d8 100644
--- a/static/shared/sidebar.js
+++ b/static/shared/sidebar.js
@@ -1,36 +1,68 @@
-/* PGΕ½ SPORT β Unified Sidebar v1.0
+/* PGΕ½ SPORT β Unified Sectioned Sidebar v2.0
* dradulic@outlook.com / damir@rinet.one β 2026-05-05
+ * Reference: app.rinet.one/klasik/dabi
*
- * Usage on each page:
- *
- * // 0 (default) = render on load. 1 = call PGZSidebar.mount() yourself
+ * Usage:
+ *
+ * // active portal hint (optional)
*
- * The script renders #pgz-sb at start of , adds class "pgz-has-sb" to body
- * (so existing layouts can be migrated). Pages that already have their own sidebar
- * should pass data-skip="1" β only NAV_EXTERNAL portal links will be appended to
- * an element with id="pgz-portal-mount" if present.
+ * Auto-mounts