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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
286 lines
12 KiB
HTML
286 lines
12 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Składki Członkowskie - Rada Izby{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.admin-header {
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.admin-header h1 {
|
|
font-size: var(--font-size-3xl);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-2xl);
|
|
}
|
|
|
|
.stat-card {
|
|
background: var(--surface);
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-card.success { border-left: 3px solid var(--success); }
|
|
.stat-card.warning { border-left: 3px solid var(--warning); }
|
|
.stat-card.primary { border-left: 3px solid var(--primary); }
|
|
|
|
.stat-value {
|
|
font-size: var(--font-size-xl);
|
|
font-weight: 700;
|
|
color: var(--primary);
|
|
}
|
|
|
|
.stat-label {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
|
|
.filters-bar {
|
|
background: var(--surface);
|
|
padding: var(--spacing-lg);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
margin-bottom: var(--spacing-xl);
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
|
|
.filters-bar select {
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.section {
|
|
background: var(--surface);
|
|
padding: var(--spacing-xl);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.fees-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
}
|
|
|
|
.fees-table th,
|
|
.fees-table td {
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
text-align: center;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.fees-table th:first-child,
|
|
.fees-table td:first-child {
|
|
text-align: left;
|
|
width: 160px;
|
|
}
|
|
|
|
.fees-table th.col-month,
|
|
.fees-table td.col-month {
|
|
width: 36px;
|
|
padding: var(--spacing-xs) 2px;
|
|
}
|
|
|
|
.fees-table th {
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
font-size: 11px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0;
|
|
background: var(--background);
|
|
}
|
|
|
|
.fees-table tr:hover {
|
|
background: var(--background);
|
|
}
|
|
|
|
.month-cell {
|
|
width: 26px;
|
|
height: 26px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 10px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.month-cell.paid { background: var(--success); color: white; }
|
|
.month-cell.partial { background: #60a5fa; color: white; }
|
|
.month-cell.pending { background: var(--warning); color: white; }
|
|
.month-cell.overdue { background: var(--error); color: white; }
|
|
.month-cell.empty { background: var(--surface-secondary); color: var(--text-secondary); }
|
|
|
|
.partial-badge {
|
|
position: absolute;
|
|
top: -6px;
|
|
right: -6px;
|
|
background: #ef4444;
|
|
color: white;
|
|
font-size: 8px;
|
|
font-weight: 700;
|
|
padding: 1px 3px;
|
|
border-radius: 6px;
|
|
line-height: 1;
|
|
transform: rotate(12deg);
|
|
min-width: 14px;
|
|
text-align: center;
|
|
}
|
|
|
|
.readonly-badge {
|
|
display: inline-block;
|
|
background: var(--info-bg);
|
|
color: var(--info);
|
|
font-size: var(--font-size-xs);
|
|
padding: 2px 8px;
|
|
border-radius: var(--radius-full);
|
|
font-weight: 600;
|
|
margin-left: var(--spacing-sm);
|
|
vertical-align: middle;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<div class="admin-header">
|
|
<h1>
|
|
<svg width="32" height="32" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
</svg>
|
|
Strefa RADA <span class="readonly-badge">tylko podgląd</span>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Board sub-navigation -->
|
|
<div style="display:flex;gap:0;margin-bottom:var(--spacing-xl);border-bottom:2px solid var(--border);">
|
|
<a href="{{ url_for('board.index') }}" style="padding:10px 20px;font-weight:500;font-size:var(--font-size-sm);text-decoration:none;border-bottom:2px solid transparent;color:var(--text-secondary);">Posiedzenia</a>
|
|
<a href="{{ url_for('board.board_fees') }}" style="padding:10px 20px;font-weight:500;font-size:var(--font-size-sm);text-decoration:none;border-bottom:2px solid var(--primary);color:var(--primary);margin-bottom:-2px;">Składki członkowskie</a>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card primary">
|
|
<div class="stat-value">{{ total_companies }}</div>
|
|
<div class="stat-label">Firm członkowskich</div>
|
|
</div>
|
|
<div class="stat-card success">
|
|
<div class="stat-value">{{ paid_count }}</div>
|
|
<div class="stat-label">Opłaconych składek (łącznie)</div>
|
|
</div>
|
|
<div class="stat-card warning">
|
|
<div class="stat-value">{{ pending_count }}</div>
|
|
<div class="stat-label">Oczekujących składek (łącznie)</div>
|
|
</div>
|
|
<div class="stat-card success">
|
|
<div class="stat-value">{{ total_paid|int }} zł</div>
|
|
<div class="stat-label">Zebrano</div>
|
|
</div>
|
|
<div class="stat-card warning">
|
|
<div class="stat-value">{{ (total_due - total_paid)|int }} zł</div>
|
|
<div class="stat-label">Do zebrania</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Legenda -->
|
|
<div style="display: flex; gap: var(--spacing-lg); flex-wrap: wrap; margin-bottom: var(--spacing-md); font-size: var(--font-size-sm); color: var(--text-secondary); align-items: center;">
|
|
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell paid" style="width: 24px; height: 24px; font-size: 11px;">1</span> Opłacone</span>
|
|
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell partial" style="width: 24px; height: 24px; font-size: 11px;">1</span> Niepełna wpłata</span>
|
|
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell pending" style="width: 24px; height: 24px; font-size: 11px;">1</span> Oczekujące</span>
|
|
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell overdue" style="width: 24px; height: 24px; font-size: 11px;">1</span> Zaległe</span>
|
|
<span style="display: flex; align-items: center; gap: 4px;"><span class="month-cell empty" style="width: 24px; height: 24px; font-size: 11px;">-</span> Brak danych</span>
|
|
<span style="display: flex; align-items: center; gap: 4px; border-left: 1px solid var(--border); padding-left: var(--spacing-lg);"><span style="color:var(--error);font-weight:700;font-size:12px;">zł</span> Zaległości z lat poprzednich</span>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="filters-bar">
|
|
<form method="GET" action="{{ url_for('board.board_fees') }}" style="display: flex; gap: var(--spacing-md); flex-wrap: wrap; align-items: center;">
|
|
<select name="year" onchange="this.form.submit()">
|
|
{% for y in years %}
|
|
<option value="{{ y }}" {% if y == year %}selected{% endif %}>{{ y }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<select name="status" onchange="this.form.submit()">
|
|
<option value="">-- Wszystkie firmy --</option>
|
|
<option value="paid" {% if status_filter == 'paid' %}selected{% endif %}>Opłacone za cały rok</option>
|
|
<option value="partial" {% if status_filter == 'partial' %}selected{% endif %}>Częściowo opłacone</option>
|
|
<option value="none" {% if status_filter == 'none' %}selected{% endif %}>Brak wpłat</option>
|
|
</select>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Companies Table -->
|
|
<div class="section">
|
|
<div class="section-header" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--spacing-lg);">
|
|
<h2>Lista firm ({{ year }}) <span style="display:inline-flex;align-items:center;justify-content:center;background:var(--error);color:white;font-size:var(--font-size-sm);font-weight:700;min-width:28px;height:28px;border-radius:var(--radius-full);padding:0 8px;vertical-align:middle;margin-left:8px;">{{ companies_fees|length }}</span></h2>
|
|
</div>
|
|
|
|
<table class="fees-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Firma</th>
|
|
<th class="col-month">I</th><th class="col-month">II</th><th class="col-month">III</th><th class="col-month">IV</th><th class="col-month">V</th><th class="col-month">VI</th>
|
|
<th class="col-month">VII</th><th class="col-month">VIII</th><th class="col-month">IX</th><th class="col-month">X</th><th class="col-month">XI</th><th class="col-month">XII</th>
|
|
<th style="width:70px;font-size:9px;line-height:1.2;">Zaległ.<br><span style="font-weight:400;text-transform:none;">z lat poprz.</span></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% set ns = namespace(separator_shown=false) %}
|
|
{% for cf in companies_fees %}
|
|
{% if not cf.has_data and not ns.separator_shown %}
|
|
{% set ns.separator_shown = true %}
|
|
<tr><td colspan="14" style="background: var(--border); padding: var(--spacing-xs); text-align: center; font-size: var(--font-size-sm); color: var(--text-secondary); font-weight: 600;">Firmy bez danych o składkach</td></tr>
|
|
{% endif %}
|
|
<tr{% if not cf.has_data %} style="opacity: 0.5;"{% endif %}>
|
|
<td>
|
|
<span {% if not cf.has_data %}style="color: var(--text-secondary);"{% endif %}>
|
|
{{ cf.company.name }}
|
|
</span>
|
|
{% if cf.monthly_rate and cf.monthly_rate > 200 %}
|
|
<span style="display:inline-block;background:#dbeafe;color:#1e40af;font-size:10px;padding:1px 5px;border-radius:3px;font-weight:600;vertical-align:middle;margin-left:4px;">{{ cf.monthly_rate }} zł</span>
|
|
{% endif %}
|
|
</td>
|
|
{% for m in range(1, 13) %}
|
|
<td class="col-month">
|
|
{% set fee = cf.months.get(m) %}
|
|
{% if fee %}
|
|
<span class="month-cell {{ fee.status }}" title="{{ fee.status }}: wpłacono {{ fee.amount_paid|int }} z {{ fee.amount|int }} zł" style="position:relative;">
|
|
{{ m }}{% if fee.status == 'partial' %}<span class="partial-badge">{{ fee.amount_paid|int }}</span>{% endif %}
|
|
</span>
|
|
{% else %}
|
|
<span class="month-cell empty" title="Brak rekordu">-</span>
|
|
{% endif %}
|
|
</td>
|
|
{% endfor %}
|
|
<td>
|
|
{% set debt = cf.company.previous_years_debt|default(0)|float %}
|
|
{% if debt > 0 %}
|
|
<span style="color:var(--error);font-weight:700;font-size:13px;">{{ debt|int }} zł</span>
|
|
{% else %}
|
|
<span style="color:var(--text-secondary);font-size:11px;">—</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|