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
Replace redundant documents tab with PDF generation from meeting data using weasyprint. Meetings become the main /rada/ view with board members section. Remove upload/view/download document routes and templates. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
284 lines
8.0 KiB
HTML
284 lines
8.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
@page {
|
|
size: A4;
|
|
margin: 25mm 20mm 25mm 20mm;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Arial, sans-serif;
|
|
font-size: 11pt;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
}
|
|
|
|
.header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
padding-bottom: 15px;
|
|
border-bottom: 2px solid #333;
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 16pt;
|
|
margin: 0 0 8px 0;
|
|
}
|
|
|
|
.header p {
|
|
color: #666;
|
|
margin: 4px 0;
|
|
font-size: 11pt;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.section h2 {
|
|
font-size: 13pt;
|
|
border-bottom: 1px solid #ddd;
|
|
padding-bottom: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.info-grid {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.info-item {
|
|
flex: 1 1 45%;
|
|
padding: 8px;
|
|
background: #f9f9f9;
|
|
}
|
|
|
|
.info-item strong {
|
|
display: block;
|
|
font-size: 9pt;
|
|
color: #666;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
th, td {
|
|
border: 1px solid #ddd;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
th {
|
|
background: #f5f5f5;
|
|
font-size: 10pt;
|
|
}
|
|
|
|
.agenda-table td:first-child {
|
|
width: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
.agenda-table td:nth-child(2) {
|
|
width: 100px;
|
|
text-align: center;
|
|
}
|
|
|
|
.proceeding {
|
|
margin-bottom: 18px;
|
|
padding: 12px;
|
|
background: #fafafa;
|
|
border-left: 3px solid #2563eb;
|
|
page-break-inside: avoid;
|
|
}
|
|
|
|
.proceeding h3 {
|
|
margin: 0 0 8px 0;
|
|
font-size: 11pt;
|
|
}
|
|
|
|
.proceeding h4 {
|
|
margin: 8px 0 4px 0;
|
|
font-size: 10pt;
|
|
color: #666;
|
|
}
|
|
|
|
.proceeding p {
|
|
margin: 0 0 6px 0;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.proceeding ul {
|
|
margin: 4px 0;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.proceeding li {
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.signature-section {
|
|
margin-top: 60px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.signature {
|
|
width: 40%;
|
|
text-align: center;
|
|
padding-top: 40px;
|
|
border-top: 1px solid #333;
|
|
font-size: 10pt;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
{% if pdf_type == 'agenda' %}
|
|
{# ==================== AGENDA PDF ==================== #}
|
|
<div class="header">
|
|
<h1>Program Posiedzenia Rady Izby Przedsiębiorców NORDA</h1>
|
|
<p>Posiedzenie nr {{ meeting.meeting_identifier }}</p>
|
|
<p>{{ meeting.meeting_date.strftime('%d.%m.%Y') }} | {{ meeting.location or 'Siedziba Izby' }}</p>
|
|
</div>
|
|
|
|
<div class="info-grid">
|
|
<div class="info-item">
|
|
<strong>Prowadzący</strong>
|
|
{{ meeting.chairperson.name if meeting.chairperson else '—' }}
|
|
</div>
|
|
{% if meeting.start_time %}
|
|
<div class="info-item">
|
|
<strong>Godzina</strong>
|
|
{{ meeting.start_time.strftime('%H:%M') }}{% if meeting.end_time %} - {{ meeting.end_time.strftime('%H:%M') }}{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if meeting.agenda_items %}
|
|
<table class="agenda-table">
|
|
<thead>
|
|
<tr><th>Lp.</th><th>Godzina</th><th>Punkt programu</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in meeting.agenda_items %}
|
|
<tr>
|
|
<td>{{ loop.index }}</td>
|
|
<td>{{ item.time_start or '—' }}{% if item.time_end %} - {{ item.time_end }}{% endif %}</td>
|
|
<td>{{ item.title }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% endif %}
|
|
|
|
{% elif pdf_type == 'protocol' %}
|
|
{# ==================== PROTOCOL PDF ==================== #}
|
|
<div class="header">
|
|
<h1>PROTOKÓŁ</h1>
|
|
<p>z Posiedzenia Rady Izby Przedsiębiorców NORDA</p>
|
|
<p>Posiedzenie nr {{ meeting.meeting_identifier }}</p>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="info-grid">
|
|
<div class="info-item"><strong>Data</strong>{{ meeting.meeting_date.strftime('%d.%m.%Y') }}</div>
|
|
<div class="info-item">
|
|
<strong>Godzina</strong>
|
|
{{ meeting.start_time.strftime('%H:%M') if meeting.start_time else '—' }}{% if meeting.end_time %} - {{ meeting.end_time.strftime('%H:%M') }}{% endif %}
|
|
</div>
|
|
<div class="info-item"><strong>Miejsce</strong>{{ meeting.location or 'Siedziba Izby' }}</div>
|
|
<div class="info-item"><strong>Prowadzący</strong>{{ meeting.chairperson.name if meeting.chairperson else '—' }}</div>
|
|
<div class="info-item"><strong>Protokolant</strong>{{ meeting.secretary.name if meeting.secretary else '—' }}</div>
|
|
<div class="info-item"><strong>Goście</strong>{{ meeting.guests or 'brak' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if meeting.attendance and board_members %}
|
|
<div class="section">
|
|
<h2>Lista obecności</h2>
|
|
<table>
|
|
<thead>
|
|
<tr><th>Lp.</th><th>Imię i nazwisko</th><th>Inicjały</th><th>Obecność</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for member in board_members %}
|
|
{% set att = meeting.attendance.get(member.id|string, {}) %}
|
|
{% set status = att.get('status', 'unknown') %}
|
|
<tr>
|
|
<td>{{ loop.index }}</td>
|
|
<td>{{ member.name or member.email.split('@')[0] }}</td>
|
|
<td>{{ att.get('initials', '') }}</td>
|
|
<td>{{ 'obecny' if status == 'present' or att.get('present') else ('nieobecny' if status == 'absent' else '—') }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% if meeting.quorum_count is not none %}
|
|
<p><strong>Kworum:</strong> {{ meeting.quorum_count }} / {{ board_members|length }} obecnych
|
|
{% if meeting.quorum_confirmed %} — Kworum osiągnięte{% else %} — Brak kworum{% endif %}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if meeting.proceedings %}
|
|
<div class="section">
|
|
<h2>Przebieg posiedzenia</h2>
|
|
{% for proc in meeting.proceedings %}
|
|
{% set agenda_item = meeting.agenda_items[proc.agenda_item] if meeting.agenda_items and proc.agenda_item < meeting.agenda_items|length else none %}
|
|
<div class="proceeding">
|
|
<h3>Ad. {{ proc.agenda_item + 1 }}. {{ proc.title or (agenda_item.title if agenda_item else 'Punkt programu') }}</h3>
|
|
|
|
{% set discussion_text = proc.discussion or proc.discussed %}
|
|
{% if discussion_text %}
|
|
<h4>Omówiono:</h4>
|
|
<p>{{ discussion_text }}</p>
|
|
{% endif %}
|
|
|
|
{% if proc.decisions is iterable and proc.decisions is not string and proc.decisions|length > 0 %}
|
|
<h4>Ustalono / decyzje:</h4>
|
|
<ul>
|
|
{% for decision in proc.decisions %}
|
|
<li>{{ decision }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% elif proc.decisions is string and proc.decisions %}
|
|
<h4>Ustalono / decyzje:</h4>
|
|
<p>{{ proc.decisions }}</p>
|
|
{% endif %}
|
|
|
|
{% if proc.tasks is iterable and proc.tasks is not string and proc.tasks|length > 0 %}
|
|
<h4>Zadania:</h4>
|
|
<ul>
|
|
{% for task in proc.tasks %}
|
|
<li>{{ task }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="signature-section">
|
|
<div class="signature">
|
|
Prowadzący posiedzenie<br>
|
|
<small>{{ meeting.chairperson.name if meeting.chairperson else '—' }}</small>
|
|
</div>
|
|
<div class="signature">
|
|
Protokolant<br>
|
|
<small>{{ meeting.secretary.name if meeting.secretary else '—' }}</small>
|
|
</div>
|
|
</div>
|
|
|
|
{% endif %}
|
|
</body>
|
|
</html>
|