DI exec: applied CC-DI Subagent A+B SQL — 3245 clanovi, Manuel Boras merged
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
/* oib_format.js — unified role-based OIB display
|
||||
* Author: Damir Radulić (damir@rinet.one / dradulic@outlook.com)
|
||||
* Date: 2026-05-05
|
||||
* Description: Single source of truth for OIB rendering across all PGŽ Sport
|
||||
* static pages. Role hierarchy (per pgz_sport.users.user_type):
|
||||
* super_admin -> full OIB everywhere
|
||||
* pgz_admin -> full OIB across PGŽ tenant
|
||||
* savez_admin -> full OIB for own savez_id (context-aware)
|
||||
* klub_admin -> full OIB for own klub_id (context-aware)
|
||||
* others -> masked: first 3 + 6 dots + last 2 (e.g. 067••••••03)
|
||||
* Usage:
|
||||
* <script src="/sport/static/oib_format.js"></script>
|
||||
* formatOib('12345678901') // role auto-detected from pgz_user
|
||||
* formatOib(o.oib, {savez_id: 1, klub_id: 7}) // pass scope for context-aware
|
||||
* maskOib('12345678901') // force masked
|
||||
*/
|
||||
(function (g) {
|
||||
'use strict';
|
||||
|
||||
var TOKEN_KEYS = ['pgz_access', 'jwt', 'access_token'];
|
||||
var USER_KEYS = ['pgz_user'];
|
||||
|
||||
// PGŽ-tier: always sees full PII for everything
|
||||
var FULL_VISIBILITY_ROLES = {
|
||||
'super_admin': 1,
|
||||
'pgz_admin': 1,
|
||||
'pgz_user': 1,
|
||||
'pgz_finance': 1,
|
||||
'pgz_zzjz': 1,
|
||||
'admin': 1 // legacy bearer token role
|
||||
};
|
||||
|
||||
// Scope-restricted roles
|
||||
var SAVEZ_ROLES = { 'savez_admin': 1, 'savez_user': 1 };
|
||||
var KLUB_ROLES = { 'klub_admin': 1, 'klub_user': 1, 'klub_trener': 1, 'klub_clan': 1 };
|
||||
|
||||
function _readUser() {
|
||||
for (var i = 0; i < USER_KEYS.length; i++) {
|
||||
var k = USER_KEYS[i];
|
||||
try {
|
||||
var raw = localStorage.getItem(k) || sessionStorage.getItem(k);
|
||||
if (raw) return JSON.parse(raw);
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _readToken() {
|
||||
for (var i = 0; i < TOKEN_KEYS.length; i++) {
|
||||
var k = TOKEN_KEYS[i];
|
||||
var t = localStorage.getItem(k) || sessionStorage.getItem(k);
|
||||
if (t) return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function _decodeJwt(tok) {
|
||||
if (!tok || tok.split('.').length !== 3) return null;
|
||||
try {
|
||||
var p = tok.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (p.length % 4) p += '=';
|
||||
return JSON.parse(atob(p));
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
/** Returns {role, klub_id, savez_id, email} from JWT or stored user. */
|
||||
function getUserCtx() {
|
||||
var u = _readUser() || {};
|
||||
var jwt = _decodeJwt(_readToken()) || {};
|
||||
var role = u.user_type || u.role || jwt.role || jwt.user_type || 'viewer';
|
||||
var klub_id = u.klub_id != null ? u.klub_id : (jwt.tenant_scope && jwt.tenant_scope.klub_id);
|
||||
var savez_id = u.savez_id != null ? u.savez_id : (jwt.tenant_scope && jwt.tenant_scope.savez_id);
|
||||
return {
|
||||
role: String(role || 'viewer').toLowerCase(),
|
||||
klub_id: klub_id == null ? null : Number(klub_id),
|
||||
savez_id: savez_id == null ? null : Number(savez_id),
|
||||
email: u.email || jwt.email || null
|
||||
};
|
||||
}
|
||||
|
||||
/** Force-masked rendering: first 3 + 6 dots + last 2. e.g. 067••••••03 */
|
||||
function maskOib(oib) {
|
||||
if (oib == null) return '—';
|
||||
var s = String(oib);
|
||||
if (s.length < 6) return '•'.repeat(s.length);
|
||||
return s.slice(0, 3) + '••••••' + s.slice(-2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether to show full OIB based on caller role and (optional) row scope.
|
||||
* @param {string|number} oib raw OIB value (or already-masked from backend)
|
||||
* @param {object} [scope] optional context: {klub_id, savez_id}
|
||||
* if provided, savez_admin / klub_admin can see
|
||||
* full OIB for rows in their own scope only
|
||||
* @returns {string} formatted OIB
|
||||
*/
|
||||
function formatOib(oib, scope) {
|
||||
if (oib == null || oib === '') return '—';
|
||||
var s = String(oib);
|
||||
// Backend already masked — pass through (we cannot un-mask client-side)
|
||||
if (s.indexOf('•') !== -1 || s.indexOf('*') !== -1) return s;
|
||||
|
||||
var ctx = getUserCtx();
|
||||
var r = ctx.role;
|
||||
|
||||
if (FULL_VISIBILITY_ROLES[r]) return s;
|
||||
|
||||
if (scope && typeof scope === 'object') {
|
||||
if (SAVEZ_ROLES[r] && ctx.savez_id != null && scope.savez_id != null
|
||||
&& Number(scope.savez_id) === ctx.savez_id) return s;
|
||||
if (KLUB_ROLES[r] && ctx.klub_id != null && scope.klub_id != null
|
||||
&& Number(scope.klub_id) === ctx.klub_id) return s;
|
||||
} else {
|
||||
// No scope passed — savez/klub admins default to full only when scope not given
|
||||
// (they would only ever query their own scope via tenanted endpoints)
|
||||
if (SAVEZ_ROLES[r] || KLUB_ROLES[r]) return s;
|
||||
}
|
||||
|
||||
return maskOib(s);
|
||||
}
|
||||
|
||||
/** Returns true if the current user can see full PII (any OIB). */
|
||||
function canSeeFullOib(scope) {
|
||||
var ctx = getUserCtx();
|
||||
if (FULL_VISIBILITY_ROLES[ctx.role]) return true;
|
||||
if (!scope) return SAVEZ_ROLES[ctx.role] || KLUB_ROLES[ctx.role] || false;
|
||||
if (SAVEZ_ROLES[ctx.role] && scope.savez_id != null && Number(scope.savez_id) === ctx.savez_id) return true;
|
||||
if (KLUB_ROLES[ctx.role] && scope.klub_id != null && Number(scope.klub_id) === ctx.klub_id) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
g.formatOib = formatOib;
|
||||
g.maskOib = maskOib;
|
||||
g.getUserCtx = g.getUserCtx || getUserCtx;
|
||||
g.canSeeFullOib = canSeeFullOib;
|
||||
})(window);
|
||||
Reference in New Issue
Block a user