feat: New users get NONE company role, admin alert for pending approvals
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

Registration now assigns company_role=NONE instead of VIEWER - users
with a company NIP must be approved by admin/office manager before
getting any company dashboard access. Admin panel shows yellow alert
banner and "Oczekujący" filter tab when users are pending approval.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-18 12:52:22 +01:00
parent c2a6d5e286
commit 29ce843849
3 changed files with 20 additions and 4 deletions

View File

@ -162,6 +162,7 @@ def admin_users():
admin_count = sum(1 for u in users if u.has_role(SystemRole.ADMIN))
verified_count = sum(1 for u in users if u.is_verified)
unverified_count = total_users - verified_count
pending_company_count = sum(1 for u in users if u.company_id and u.company_role == 'NONE')
logger.info(f"Admin {current_user.email} accessed users panel - {total_users} users")
@ -172,7 +173,8 @@ def admin_users():
total_users=total_users,
admin_count=admin_count,
verified_count=verified_count,
unverified_count=unverified_count
unverified_count=unverified_count,
pending_company_count=pending_company_count
)
finally:
db.close()

View File

@ -157,7 +157,7 @@ def register():
name=name,
company_nip=company_nip,
company_id=company_id,
company_role='VIEWER',
company_role='NONE',
is_norda_member=is_norda_member,
created_at=datetime.now(),
is_active=True,
@ -171,7 +171,7 @@ def register():
# Create user_companies record if company matched
if company_id:
uc = UserCompany(user_id=user.id, company_id=company_id, role='VIEWER', is_primary=True)
uc = UserCompany(user_id=user.id, company_id=company_id, role='NONE', is_primary=True)
db.add(uc)
db.commit()

View File

@ -1101,6 +1101,13 @@
</div>
</div>
{% if pending_company_count > 0 %}
<div style="background: #FEF3C7; border: 1px solid #F59E0B; border-radius: var(--radius); padding: var(--spacing-md) var(--spacing-lg); margin-bottom: var(--spacing-lg); display: flex; align-items: center; gap: var(--spacing-md);">
<span style="font-size: 1.3em;">⚠️</span>
<span><strong>{{ pending_company_count }}</strong> {{ 'użytkownik czeka' if pending_company_count == 1 else 'użytkowników czeka' }} na zatwierdzenie uprawnień firmowych (firma przypisana, rola: Brak)</span>
</div>
{% endif %}
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card">
@ -1131,6 +1138,9 @@
<button class="filter-tab" data-filter="admin">Admini ({{ admin_count }})</button>
<button class="filter-tab" data-filter="verified">Zweryfikowani ({{ verified_count }})</button>
<button class="filter-tab" data-filter="unverified">Niezweryfikowani ({{ unverified_count }})</button>
{% if pending_company_count > 0 %}
<button class="filter-tab" data-filter="pending-company" style="color: #D97706;">Oczekujący ({{ pending_company_count }})</button>
{% endif %}
</div>
{% if users %}
@ -1152,7 +1162,8 @@
{% for user in users %}
<tr data-user-id="{{ user.id }}"
data-role="{{ user.role }}"
data-is-verified="{{ 'true' if user.is_verified else 'false' }}">
data-is-verified="{{ 'true' if user.is_verified else 'false' }}"
data-pending-company="{{ 'true' if user.company_id and user.company_role == 'NONE' else 'false' }}">
<td>{{ user.id }}</td>
<td>
<div class="user-info">
@ -1847,10 +1858,13 @@ Lub format CSV, Excel, lista emaili..."></textarea>
const isVerified = row.dataset.isVerified === 'true';
let show = false;
const isPendingCompany = row.dataset.pendingCompany === 'true';
if (filter === 'all') show = true;
else if (filter === 'admin') show = isAdmin;
else if (filter === 'verified') show = isVerified;
else if (filter === 'unverified') show = !isVerified;
else if (filter === 'pending-company') show = isPendingCompany;
row.style.display = show ? '' : 'none';
});