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
Production moved from on-prem VM 249 (10.22.68.249) to OVH VPS (57.128.200.27, inpi-vps-waw01). Updated ALL documentation, slash commands, memory files, architecture docs, and deploy procedures. Added |local_time Jinja filter (UTC→Europe/Warsaw) and converted 155 .strftime() calls across 71 templates so timestamps display in Polish timezone regardless of server timezone. Also includes: created_by_id tracking, abort import fix, ICS calendar fix for missing end times, Pros Poland data cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
381 lines
11 KiB
HTML
381 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Zgłoszenia Uzupełnienia Danych - Admin - Norda Biznes Partner{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.admin-header {
|
|
margin-bottom: var(--spacing-xl);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.admin-header-content h1 {
|
|
font-size: var(--font-size-3xl);
|
|
color: var(--text-primary);
|
|
margin: 0;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-2xl);
|
|
}
|
|
|
|
.stat-card {
|
|
background: var(--surface);
|
|
padding: var(--spacing-lg);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: var(--font-size-3xl);
|
|
font-weight: 700;
|
|
}
|
|
|
|
.stat-card.pending .stat-value { color: var(--warning); }
|
|
.stat-card.approved .stat-value { color: var(--success); }
|
|
.stat-card.rejected .stat-value { color: var(--error); }
|
|
|
|
.stat-label {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
|
|
.filters-row {
|
|
display: flex;
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-lg);
|
|
}
|
|
|
|
.filter-select {
|
|
padding: var(--spacing-xs) var(--spacing-sm);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
font-size: var(--font-size-sm);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.section {
|
|
background: var(--surface);
|
|
padding: var(--spacing-xl);
|
|
border-radius: var(--radius-lg);
|
|
box-shadow: var(--shadow);
|
|
}
|
|
|
|
.section h2 {
|
|
font-size: var(--font-size-xl);
|
|
margin-bottom: var(--spacing-lg);
|
|
color: var(--text-primary);
|
|
border-bottom: 2px solid var(--border);
|
|
padding-bottom: var(--spacing-sm);
|
|
}
|
|
|
|
.request-card {
|
|
background: var(--background);
|
|
padding: var(--spacing-lg);
|
|
border-radius: var(--radius);
|
|
margin-bottom: var(--spacing-md);
|
|
border-left: 4px solid var(--warning);
|
|
}
|
|
|
|
.request-card.approved {
|
|
border-color: var(--success);
|
|
}
|
|
|
|
.request-card.rejected {
|
|
border-color: var(--error);
|
|
}
|
|
|
|
.request-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.request-company {
|
|
font-weight: 600;
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
.request-nip {
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-flex;
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
border-radius: var(--radius-full);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.status-pending { background: var(--warning-light); color: var(--warning); }
|
|
.status-approved { background: var(--success-light); color: var(--success); }
|
|
.status-rejected { background: var(--error-light); color: var(--error); }
|
|
|
|
.request-info {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: var(--spacing-md);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.info-item {
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.info-label {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.registry-data {
|
|
background: var(--surface);
|
|
padding: var(--spacing-md);
|
|
border-radius: var(--radius);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.registry-data h4 {
|
|
margin: 0 0 var(--spacing-sm) 0;
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.registry-data-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: var(--spacing-xs);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.request-actions {
|
|
display: flex;
|
|
gap: var(--spacing-sm);
|
|
}
|
|
|
|
.btn-approve, .btn-reject {
|
|
padding: var(--spacing-xs) var(--spacing-md);
|
|
border-radius: var(--radius);
|
|
font-size: var(--font-size-sm);
|
|
cursor: pointer;
|
|
border: none;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.btn-approve {
|
|
background: var(--success);
|
|
color: white;
|
|
}
|
|
|
|
.btn-reject {
|
|
background: var(--error);
|
|
color: white;
|
|
}
|
|
|
|
.btn-approve:hover, .btn-reject:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: var(--spacing-2xl);
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--spacing-xs);
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
margin-bottom: var(--spacing-lg);
|
|
}
|
|
|
|
.back-link:hover {
|
|
color: var(--primary);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<a href="{{ url_for('admin.admin_membership') }}" class="back-link">
|
|
← Powrót do deklaracji członkowskich
|
|
</a>
|
|
|
|
<div class="admin-header">
|
|
<div class="admin-header-content">
|
|
<h1>Zgłoszenia Uzupełnienia Danych</h1>
|
|
<p class="text-muted">Weryfikacja danych z KRS/CEIDG dla istniejących firm</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card pending">
|
|
<div class="stat-value">{{ pending }}</div>
|
|
<div class="stat-label">Oczekujące</div>
|
|
</div>
|
|
<div class="stat-card approved">
|
|
<div class="stat-value">{{ approved }}</div>
|
|
<div class="stat-label">Zatwierdzone</div>
|
|
</div>
|
|
<div class="stat-card rejected">
|
|
<div class="stat-value">{{ rejected }}</div>
|
|
<div class="stat-label">Odrzucone</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="GET" class="filters-row">
|
|
<select name="status" class="filter-select" onchange="this.form.submit()">
|
|
<option value="pending" {% if current_status == 'pending' %}selected{% endif %}>Oczekujące</option>
|
|
<option value="approved" {% if current_status == 'approved' %}selected{% endif %}>Zatwierdzone</option>
|
|
<option value="rejected" {% if current_status == 'rejected' %}selected{% endif %}>Odrzucone</option>
|
|
<option value="all" {% if current_status == 'all' %}selected{% endif %}>Wszystkie</option>
|
|
</select>
|
|
</form>
|
|
|
|
<div class="section">
|
|
<h2>Zgłoszenia ({{ requests|length }})</h2>
|
|
|
|
{% if requests %}
|
|
{% for req in requests %}
|
|
<div class="request-card {{ req.status }}">
|
|
<div class="request-header">
|
|
<div>
|
|
<div class="request-company">
|
|
{{ req.company.name if req.company else 'Nieznana firma' }}
|
|
</div>
|
|
<div class="request-nip">NIP: {{ req.nip }}</div>
|
|
</div>
|
|
<span class="status-badge status-{{ req.status }}">
|
|
{{ req.status_label }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="request-info">
|
|
<div class="info-item">
|
|
<span class="info-label">Zgłaszający:</span>
|
|
{{ req.user.name if req.user else '-' }} ({{ req.user.email if req.user else '-' }})
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">Typ:</span>
|
|
{{ req.request_type_label }}
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">Data:</span>
|
|
{{ req.created_at|local_time('%Y-%m-%d %H:%M') if req.created_at else '-' }}
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">Źródło:</span>
|
|
{{ req.registry_source or 'Brak' }}
|
|
</div>
|
|
</div>
|
|
|
|
{% if req.fetched_data %}
|
|
<div class="registry-data">
|
|
<h4>Dane z rejestru {{ req.registry_source }}:</h4>
|
|
<div class="registry-data-grid">
|
|
{% if req.fetched_data.name %}
|
|
<div><strong>Nazwa:</strong> {{ req.fetched_data.name }}</div>
|
|
{% endif %}
|
|
{% if req.fetched_data.regon %}
|
|
<div><strong>REGON:</strong> {{ req.fetched_data.regon }}</div>
|
|
{% endif %}
|
|
{% if req.fetched_data.krs %}
|
|
<div><strong>KRS:</strong> {{ req.fetched_data.krs }}</div>
|
|
{% endif %}
|
|
{% if req.fetched_data.address_city %}
|
|
<div><strong>Miasto:</strong> {{ req.fetched_data.address_city }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if req.user_note %}
|
|
<div style="margin-bottom: var(--spacing-md);">
|
|
<strong>Notatka:</strong> {{ req.user_note }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if req.status == 'pending' %}
|
|
<div class="request-actions">
|
|
<button class="btn-approve" onclick="approveRequest({{ req.id }})">
|
|
✓ Zatwierdź i zaktualizuj
|
|
</button>
|
|
<button class="btn-reject" onclick="rejectRequest({{ req.id }})">
|
|
✗ Odrzuć
|
|
</button>
|
|
</div>
|
|
{% elif req.review_comment %}
|
|
<div style="font-size: var(--font-size-sm); color: var(--text-secondary);">
|
|
<strong>Komentarz:</strong> {{ req.review_comment }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if req.applied_fields %}
|
|
<div style="font-size: var(--font-size-sm); color: var(--success); margin-top: var(--spacing-sm);">
|
|
<strong>Zaktualizowane pola:</strong> {{ req.applied_fields|join(', ') }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<p>Brak zgłoszeń w wybranej kategorii.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
async function approveRequest(id) {
|
|
if (!confirm('Czy na pewno chcesz zatwierdzić to zgłoszenie i zaktualizować dane firmy?')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/admin/company-requests/${id}/approve`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
alert('Zgłoszenie zatwierdzone. Zaktualizowane pola: ' + (result.applied_fields || []).join(', '));
|
|
location.reload();
|
|
} else {
|
|
alert(result.error || 'Błąd');
|
|
}
|
|
} catch (e) {
|
|
alert('Błąd połączenia');
|
|
}
|
|
}
|
|
|
|
async function rejectRequest(id) {
|
|
const comment = prompt('Podaj powód odrzucenia (opcjonalnie):');
|
|
if (comment === null) return;
|
|
|
|
try {
|
|
const response = await fetch(`/admin/company-requests/${id}/reject`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ comment: comment })
|
|
});
|
|
const result = await response.json();
|
|
if (result.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(result.error || 'Błąd');
|
|
}
|
|
} catch (e) {
|
|
alert('Błąd połączenia');
|
|
}
|
|
}
|
|
{% endblock %}
|