feat(admin): Enhance access overview with full permissions matrix and collapsible grouping
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Comprehensive rewrite of access control dashboard: role hierarchy table, full function-by-role access matrix (7 columns incl. unauthenticated users), Rada Izby members section, and all active accounts with Excel-like collapsible grouping by role. Expand/collapse all controls included. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7f77d7ebcd
commit
b630186920
@ -744,16 +744,28 @@ def admin_access_overview():
|
||||
abort(404)
|
||||
|
||||
from database import User
|
||||
from sqlalchemy import func
|
||||
db = SessionLocal()
|
||||
try:
|
||||
users = db.query(User).filter(
|
||||
User.is_active == True,
|
||||
User.role.in_(['OFFICE_MANAGER', 'ADMIN'])
|
||||
).order_by(User.name).all()
|
||||
# All active users
|
||||
all_users = db.query(User).filter(
|
||||
User.is_active == True
|
||||
).order_by(User.role.desc(), User.name).all()
|
||||
|
||||
# Role distribution
|
||||
role_counts = {}
|
||||
for user in all_users:
|
||||
role = user.role or 'UNAFFILIATED'
|
||||
role_counts[role] = role_counts.get(role, 0) + 1
|
||||
|
||||
# Rada Izby members
|
||||
rada_members = [u for u in all_users if u.is_rada_member]
|
||||
|
||||
from utils.decorators import AUDIT_OWNER_EMAIL
|
||||
return render_template('admin/access_overview.html',
|
||||
users=users,
|
||||
all_users=all_users,
|
||||
role_counts=role_counts,
|
||||
rada_members=rada_members,
|
||||
audit_owner_email=AUDIT_OWNER_EMAIL
|
||||
)
|
||||
finally:
|
||||
|
||||
@ -3,92 +3,537 @@
|
||||
{% block title %}Kontrola dostepu - Admin{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.access-matrix { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.access-matrix th, .access-matrix td { padding: 6px 8px; border: 1px solid #e2e8f0; }
|
||||
.access-matrix th { background: #f8fafc; font-weight: 600; white-space: nowrap; }
|
||||
.access-matrix td { text-align: center; }
|
||||
.access-matrix td:first-child { text-align: left; font-weight: 500; }
|
||||
.access-yes { color: #16a34a; font-weight: bold; }
|
||||
.access-no { color: #dc2626; }
|
||||
.access-partial { color: #d97706; font-weight: bold; }
|
||||
.access-matrix tr.row-highlight { background: #eff6ff; }
|
||||
.access-matrix tr.row-separator td { background: #1e40af; color: white; font-weight: 600; text-align: left; padding: 8px; }
|
||||
.role-badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
|
||||
.role-ADMIN { background: #fee2e2; color: #991b1b; }
|
||||
.role-OFFICE_MANAGER { background: #fef3c7; color: #92400e; }
|
||||
.role-MANAGER { background: #dbeafe; color: #1e40af; }
|
||||
.role-EMPLOYEE { background: #dcfce7; color: #166534; }
|
||||
.role-MEMBER { background: #e0e7ff; color: #3730a3; }
|
||||
.role-UNAFFILIATED { background: #f1f5f9; color: #475569; }
|
||||
.section-card { margin-bottom: var(--spacing-lg); }
|
||||
.section-card h3 { margin: 0 0 var(--spacing-sm) 0; }
|
||||
.group-header:hover { background: #cbd5e1 !important; }
|
||||
.group-header td { background: #e2e8f0; }
|
||||
.group-toggle { display: inline-block; width: 16px; font-size: 11px; transition: transform 0.15s; }
|
||||
.group-toggle.collapsed { transform: rotate(-90deg); }
|
||||
.group-row.hidden { display: none; }
|
||||
</style>
|
||||
|
||||
<div class="admin-container">
|
||||
<div class="page-header">
|
||||
<h1>Kontrola dostepu</h1>
|
||||
<h1>Kontrola dostepu do portalu</h1>
|
||||
<p style="color: var(--text-secondary); margin-top: var(--spacing-xs);">
|
||||
Matryca dostepu do funkcji audytowych
|
||||
Pelna matryca uprawnien — wszystkie role, funkcje i konta
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Rule explanation -->
|
||||
<div class="card" style="margin-bottom: var(--spacing-lg); background: #eff6ff; border: 1px solid #bfdbfe;">
|
||||
<!-- 1. ROLE HIERARCHY -->
|
||||
<div class="card section-card">
|
||||
<div style="padding: var(--spacing-md);">
|
||||
<h3 style="margin: 0 0 var(--spacing-sm) 0; color: #1e40af;">Zasada ograniczenia dostepu</h3>
|
||||
<p style="margin: 0; color: #1e3a5f;">
|
||||
Audyty SEO, IT, GBP i Social Media sa widoczne wylacznie dla <strong>{{ audit_owner_email }}</strong>.
|
||||
Pozostali administratorzy nie widza tych funkcji w menu ani na stronach firm.
|
||||
Audyt KRS i Digital Maturity pozostaja dostepne dla wszystkich z rola OFFICE_MANAGER+.
|
||||
<h3>Hierarchia rol ({{ all_users|length }} aktywnych kont)</h3>
|
||||
<table class="access-matrix">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rola</th>
|
||||
<th>Poziom</th>
|
||||
<th>Opis</th>
|
||||
<th>Kont</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><span class="role-badge role-ADMIN">ADMIN</span></td>
|
||||
<td style="text-align:center;">100</td>
|
||||
<td style="text-align:left;">Administrator portalu — pelne prawa</td>
|
||||
<td>{{ role_counts.get('ADMIN', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="role-badge role-OFFICE_MANAGER">OFFICE_MANAGER</span></td>
|
||||
<td style="text-align:center;">50</td>
|
||||
<td style="text-align:left;">Kierownik biura Norda — panel admina</td>
|
||||
<td>{{ role_counts.get('OFFICE_MANAGER', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="role-badge role-MANAGER">MANAGER</span></td>
|
||||
<td style="text-align:center;">40</td>
|
||||
<td style="text-align:left;">Kadra zarzadzajaca — pelna kontrola firmy + uzytkownicy</td>
|
||||
<td>{{ role_counts.get('MANAGER', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="role-badge role-EMPLOYEE">EMPLOYEE</span></td>
|
||||
<td style="text-align:center;">30</td>
|
||||
<td style="text-align:left;">Pracownik firmy czlonkowskiej — edycja danych firmy</td>
|
||||
<td>{{ role_counts.get('EMPLOYEE', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="role-badge role-MEMBER">MEMBER</span></td>
|
||||
<td style="text-align:center;">20</td>
|
||||
<td style="text-align:left;">Czlonek Norda bez firmy — pelny dostep do tresci</td>
|
||||
<td>{{ role_counts.get('MEMBER', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class="role-badge role-UNAFFILIATED">UNAFFILIATED</span></td>
|
||||
<td style="text-align:center;">10</td>
|
||||
<td style="text-align:left;">Firma spoza Izby — tylko publiczne profile (bez kontaktow)</td>
|
||||
<td>{{ role_counts.get('UNAFFILIATED', 0) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: var(--text-secondary);"><em>Niezalogowany</em></td>
|
||||
<td style="text-align:center;">0</td>
|
||||
<td style="text-align:left;">Odwiedzajacy bez konta — strona glowna, katalog, rejestracja</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. FULL ACCESS MATRIX BY ROLE -->
|
||||
<div class="card section-card">
|
||||
<div style="padding: var(--spacing-md); overflow-x: auto;">
|
||||
<h3>Matryca dostepu do funkcji portalu</h3>
|
||||
<table class="access-matrix">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="min-width: 220px;">Funkcja</th>
|
||||
<th>Niezalogowany</th>
|
||||
<th>UNAFFILIATED</th>
|
||||
<th>MEMBER</th>
|
||||
<th>EMPLOYEE</th>
|
||||
<th>MANAGER</th>
|
||||
<th>OFFICE_MGR</th>
|
||||
<th>ADMIN</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- PUBLIC -->
|
||||
<tr class="row-separator"><td colspan="8">Strony publiczne</td></tr>
|
||||
<tr>
|
||||
<td>Katalog firm (lista, profil)</td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kontakty firmy (email, telefon)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NordaGPT (chat AI)</td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Aktualnosci, ZOPK, Raporty</td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wyszukiwarka</td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rejestracja / logowanie</td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
|
||||
<!-- COMMUNITY -->
|
||||
<tr class="row-separator"><td colspan="8">Spolecznosc (wymaga logowania)</td></tr>
|
||||
<tr>
|
||||
<td>Panel uzytkownika (dashboard)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Forum (czytanie + pisanie)</td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wiadomosci prywatne</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tablica ogloszen B2B</td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kalendarz wydarzen (zapisy)</td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edukacja</td>
|
||||
<td><span class="access-partial">odczyt</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Korzysci czlonkowskie</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
|
||||
<!-- COMPANY MANAGEMENT -->
|
||||
<tr class="row-separator"><td colspan="8">Zarzadzanie firma</td></tr>
|
||||
<tr>
|
||||
<td>Edycja profilu firmy</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-partial">kazda</span></td>
|
||||
<td><span class="access-partial">kazda</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Formularz audytu IT (firma)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
|
||||
<!-- RADA IZBY -->
|
||||
<tr class="row-separator"><td colspan="8">Rada Izby ({{ rada_members|length }} czlonkow)</td></tr>
|
||||
<tr>
|
||||
<td>Strefa Rady (/rada)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Posiedzenia Rady (dokumenty)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wydarzenia typu "rada" (lista uczestnikow)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-partial">rada</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
|
||||
<!-- AUDITS -->
|
||||
<tr class="row-separator"><td colspan="8">Audyty (ograniczone)</td></tr>
|
||||
<tr>
|
||||
<td>Audyt SEO (admin + per-firma + API)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Audyt IT (admin + per-firma + API)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Audyt GBP (admin + per-firma + API)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Audyt Social Media (admin + per-firma + API)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Audyt KRS</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Digital Maturity</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
|
||||
<!-- ADMIN -->
|
||||
<tr class="row-separator"><td colspan="8">Panel administracyjny</td></tr>
|
||||
<tr>
|
||||
<td>Admin bar (nawigacja)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Zarzadzanie (firmy, uzytkownicy, skladki)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Analityka, ZOPK admin, Forum admin</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Moderacja forum</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bezpieczenstwo, Debug, Status</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
<td><span class="access-yes">✓</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kontrola dostepu (ta strona)</td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-no">✗</span></td>
|
||||
<td><span class="access-partial">*owner</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="margin-top: var(--spacing-sm); font-size: 12px; color: var(--text-secondary);">
|
||||
<span class="access-partial">*owner</span> = tylko {{ audit_owner_email }} |
|
||||
<span class="access-partial">rada</span> = tylko czlonkowie Rady Izby (is_rada_member) |
|
||||
<span class="access-partial">kazda</span> = moze edytowac dowolna firme |
|
||||
<span class="access-partial">odczyt</span> = tylko przegladanie
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Access matrix -->
|
||||
<div class="card">
|
||||
<div style="padding: var(--spacing-md); overflow-x: auto;">
|
||||
<table class="data-table" style="width: 100%;">
|
||||
<!-- 3. RADA IZBY MEMBERS -->
|
||||
{% if rada_members %}
|
||||
<div class="card section-card">
|
||||
<div style="padding: var(--spacing-md);">
|
||||
<h3>Czlonkowie Rady Izby ({{ rada_members|length }})</h3>
|
||||
<table class="access-matrix">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Uzytkownik</th>
|
||||
<th>Imie i nazwisko</th>
|
||||
<th>Email</th>
|
||||
<th>Rola</th>
|
||||
<th style="text-align: center;">Audyt SEO</th>
|
||||
<th style="text-align: center;">Audyt IT</th>
|
||||
<th style="text-align: center;">Audyt GBP</th>
|
||||
<th style="text-align: center;">Audyt Social</th>
|
||||
<th style="text-align: center;">Audyt KRS</th>
|
||||
<th style="text-align: center;">Digital Maturity</th>
|
||||
<th style="text-align: center;">Inne admin</th>
|
||||
<th>Rola systemowa</th>
|
||||
<th>Firma</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
{% for user in rada_members %}
|
||||
<tr>
|
||||
<td><strong>{{ user.name or 'Brak nazwy' }}</strong></td>
|
||||
<td><strong>{{ user.name or '—' }}</strong></td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ 'success' if user.role == 'ADMIN' else 'info' }}">
|
||||
{{ user.role }}
|
||||
</span>
|
||||
<td><span class="role-badge role-{{ user.role }}">{{ user.role }}</span></td>
|
||||
<td>{{ user.company.name if user.company else '—' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- 4. ALL USERS (grouped by role, collapsible) -->
|
||||
<div class="card section-card">
|
||||
<div style="padding: var(--spacing-md); overflow-x: auto;">
|
||||
<h3>Wszystkie aktywne konta ({{ all_users|length }})</h3>
|
||||
<p style="font-size: 12px; color: var(--text-secondary); margin-bottom: var(--spacing-sm);">
|
||||
Kliknij naglowek grupy, aby zwinac/rozwinac |
|
||||
<a href="#" onclick="toggleAllGroups(true); return false;">Rozwin wszystkie</a> |
|
||||
<a href="#" onclick="toggleAllGroups(false); return false;">Zwin wszystkie</a>
|
||||
</p>
|
||||
<table class="access-matrix" id="users-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Imie i nazwisko</th>
|
||||
<th>Email</th>
|
||||
<th>Rola</th>
|
||||
<th style="text-align: center;">Rada</th>
|
||||
<th>Firma</th>
|
||||
<th style="text-align: center;">Audyty</th>
|
||||
<th style="text-align: center;">Admin</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set current_role = namespace(value='') %}
|
||||
{% for user in all_users %}
|
||||
{% if user.role != current_role.value %}
|
||||
{% set current_role.value = user.role %}
|
||||
<tr class="group-header" data-group="{{ user.role }}" onclick="toggleGroup('{{ user.role }}')" style="cursor: pointer;">
|
||||
<td colspan="7" style="background: #e2e8f0; font-weight: 700; padding: 8px;">
|
||||
<span class="group-toggle" id="toggle-{{ user.role }}">▼</span>
|
||||
<span class="role-badge role-{{ user.role }}" style="margin: 0 8px;">{{ user.role }}</span>
|
||||
— {{ role_counts.get(user.role, 0) }} kont
|
||||
</td>
|
||||
{% set is_owner = user.email == audit_owner_email %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr class="group-row group-{{ user.role }}"{% if user.email == audit_owner_email %} style="background: #eff6ff;"{% endif %}>
|
||||
<td><strong>{{ user.name or '—' }}</strong></td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td><span class="role-badge role-{{ user.role }}">{{ user.role }}</span></td>
|
||||
<td style="text-align: center;">
|
||||
{% if is_owner %}
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
{% if user.is_rada_member %}
|
||||
<span class="access-yes">✓</span>
|
||||
{% else %}
|
||||
<span style="color: #dc2626;" title="Brak dostepu">✗</span>
|
||||
<span style="color: #cbd5e1;">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ user.company.name if user.company else '—' }}</td>
|
||||
<td style="text-align: center;">
|
||||
{% if user.email == audit_owner_email %}
|
||||
<span class="access-yes">✓</span>
|
||||
{% else %}
|
||||
<span class="access-no">✗</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if is_owner %}
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
{% if user.role in ('OFFICE_MANAGER', 'ADMIN') %}
|
||||
<span class="access-yes">✓</span>
|
||||
{% else %}
|
||||
<span style="color: #dc2626;" title="Brak dostepu">✗</span>
|
||||
<span class="access-no">✗</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if is_owner %}
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
{% else %}
|
||||
<span style="color: #dc2626;" title="Brak dostepu">✗</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if is_owner %}
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
{% else %}
|
||||
<span style="color: #dc2626;" title="Brak dostepu">✗</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<span style="color: #16a34a; font-weight: bold;" title="Dostep">✓</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@ -97,15 +542,48 @@
|
||||
</div>
|
||||
|
||||
<!-- Reversibility note -->
|
||||
<div class="card" style="margin-top: var(--spacing-lg); background: #fefce8; border: 1px solid #fde68a;">
|
||||
<div class="card" style="background: #fefce8; border: 1px solid #fde68a;">
|
||||
<div style="padding: var(--spacing-md);">
|
||||
<h3 style="margin: 0 0 var(--spacing-sm) 0; color: #92400e;">Odwracalnosc</h3>
|
||||
<p style="margin: 0; color: #78350f;">
|
||||
Aby przywrocic dostep do audytow dla wszystkich administratorow,
|
||||
nalezy zmienic funkcje <code>is_audit_owner()</code> w pliku
|
||||
<code>utils/decorators.py</code> na sprawdzanie roli OFFICE_MANAGER.
|
||||
<code>utils/decorators.py</code> na <code>return current_user.has_role(SystemRole.OFFICE_MANAGER)</code>.
|
||||
Jeden plik, jedna linia.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
function toggleGroup(role) {
|
||||
var rows = document.querySelectorAll('.group-' + role);
|
||||
var toggle = document.getElementById('toggle-' + role);
|
||||
var isCollapsed = toggle.classList.contains('collapsed');
|
||||
rows.forEach(function(row) {
|
||||
if (isCollapsed) {
|
||||
row.classList.remove('hidden');
|
||||
} else {
|
||||
row.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
toggle.classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
function toggleAllGroups(expand) {
|
||||
var headers = document.querySelectorAll('.group-header');
|
||||
headers.forEach(function(header) {
|
||||
var role = header.getAttribute('data-group');
|
||||
var toggle = document.getElementById('toggle-' + role);
|
||||
var rows = document.querySelectorAll('.group-' + role);
|
||||
if (expand) {
|
||||
toggle.classList.remove('collapsed');
|
||||
rows.forEach(function(r) { r.classList.remove('hidden'); });
|
||||
} else {
|
||||
toggle.classList.add('collapsed');
|
||||
rows.forEach(function(r) { r.classList.add('hidden'); });
|
||||
}
|
||||
});
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user