feat(pej): add PEJ templates — landing page, local content, news

Purple/indigo color scheme (#7c3aed). Responsive grid layouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-16 18:22:09 +01:00
parent 4edab133d3
commit 7508823cac
3 changed files with 463 additions and 0 deletions

222
templates/pej/index.html Normal file
View File

@ -0,0 +1,222 @@
{% extends "base.html" %}
{% block title %}PEJ — Elektrownia Jądrowa - Norda Biznes Partner{% endblock %}
{% block extra_css %}
<style>
.pej-hero {
background: #7c3aed;
color: #fff;
padding: var(--spacing-2xl) 0;
margin: calc(-1 * var(--spacing-lg)) calc(-1 * var(--spacing-lg)) var(--spacing-xl);
text-align: center;
}
.pej-hero h1 { font-size: 2rem; margin-bottom: var(--spacing-sm); }
.pej-hero p { opacity: 0.9; max-width: 600px; margin: 0 auto; font-size: var(--font-size-md); }
.pej-stats {
display: flex; justify-content: center; gap: var(--spacing-xl);
padding: var(--spacing-md) 0;
margin-bottom: var(--spacing-xl);
}
.pej-stat { text-align: center; }
.pej-stat-value { font-size: 2rem; font-weight: 700; color: #7c3aed; }
.pej-stat-label { font-size: var(--font-size-sm); color: var(--text-secondary); }
.pej-section { margin-bottom: var(--spacing-xl); }
.pej-section-title {
font-size: var(--font-size-lg); font-weight: 600;
margin-bottom: var(--spacing-md);
display: flex; align-items: center; gap: var(--spacing-sm);
}
.pej-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-lg); }
@media (max-width: 768px) { .pej-grid { grid-template-columns: 1fr; } }
.pej-announcement {
background: #f5f3ff; border: 1px solid #e9d5ff; border-radius: var(--radius);
padding: var(--spacing-md); margin-bottom: var(--spacing-sm);
}
.pej-announcement-title { font-weight: 600; margin-bottom: var(--spacing-xs); }
.pej-announcement-date { font-size: var(--font-size-sm); color: var(--text-secondary); }
.pej-news-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: var(--spacing-md);
margin-bottom: var(--spacing-sm); transition: var(--transition);
}
.pej-news-card:hover { border-color: #7c3aed; }
.pej-news-card h3 { font-size: var(--font-size-md); margin-bottom: var(--spacing-xs); }
.pej-news-card h3 a { color: var(--text-primary); text-decoration: none; }
.pej-news-card h3 a:hover { color: #7c3aed; }
.pej-news-card .meta { font-size: var(--font-size-sm); color: var(--text-secondary); }
.pej-milestone {
display: flex; gap: var(--spacing-md); padding: var(--spacing-sm) 0;
border-bottom: 1px solid var(--border);
}
.pej-milestone:last-child { border-bottom: none; }
.pej-milestone-date {
min-width: 80px; font-size: var(--font-size-sm); color: #7c3aed; font-weight: 600;
}
.pej-milestone-title { font-size: var(--font-size-sm); }
.pej-milestone-status {
font-size: 0.7rem; padding: 2px 6px; border-radius: 10px;
display: inline-block; margin-left: var(--spacing-xs);
}
.pej-milestone-status.planned { background: #e0e7ff; color: #3730a3; }
.pej-milestone-status.in_progress { background: #fef3c7; color: #92400e; }
.pej-milestone-status.completed { background: #d1fae5; color: #065f46; }
.pej-milestone-status.delayed { background: #fee2e2; color: #991b1b; }
.pej-company-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-md); }
@media (max-width: 768px) { .pej-company-grid { grid-template-columns: 1fr; } }
@media (min-width: 769px) and (max-width: 1024px) { .pej-company-grid { grid-template-columns: repeat(2, 1fr); } }
.pej-company-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: var(--spacing-md);
transition: var(--transition);
}
.pej-company-card:hover { border-color: #7c3aed; box-shadow: 0 2px 8px rgba(124,58,237,0.1); }
.pej-company-name { font-weight: 600; margin-bottom: var(--spacing-xs); }
.pej-company-name a { color: var(--text-primary); text-decoration: none; }
.pej-company-name a:hover { color: #7c3aed; }
.pej-company-meta { font-size: var(--font-size-sm); color: var(--text-secondary); }
.pej-company-score {
display: inline-block; background: #f5f3ff; color: #7c3aed;
padding: 2px 8px; border-radius: 10px; font-size: var(--font-size-sm); font-weight: 600;
}
.pej-company-type {
display: inline-block; background: #e0e7ff; color: #3730a3;
padding: 2px 8px; border-radius: 10px; font-size: 0.75rem;
}
.pej-company-desc {
font-size: var(--font-size-sm); color: var(--text-secondary);
margin-top: var(--spacing-xs);
}
.pej-cta {
text-align: center; margin-top: var(--spacing-lg);
display: flex; gap: var(--spacing-md); justify-content: center; flex-wrap: wrap;
}
.pej-btn {
display: inline-block; padding: var(--spacing-sm) var(--spacing-lg);
border-radius: var(--radius); text-decoration: none; font-weight: 500;
transition: var(--transition);
}
.pej-btn-primary { background: #7c3aed; color: #fff; }
.pej-btn-primary:hover { background: #6d28d9; }
.pej-btn-outline { border: 1px solid #7c3aed; color: #7c3aed; }
.pej-btn-outline:hover { background: #f5f3ff; }
.pej-empty { text-align: center; padding: var(--spacing-xl); color: var(--text-secondary); }
</style>
{% endblock %}
{% block content %}
<div class="pej-hero">
<h1>Elektrownia Jądrowa — Szanse dla Naszych Firm</h1>
<p>Izba Norda Biznes aktywnie uczestniczy w projekcie PEJ. Tu znajdziesz aktualności, listę firm gotowych do współpracy i informacje o możliwościach dla członków.</p>
</div>
<div class="pej-stats">
<div class="pej-stat">
<div class="pej-stat-value">{{ companies_count }}</div>
<div class="pej-stat-label">firm gotowych</div>
</div>
<div class="pej-stat">
<div class="pej-stat-value">{{ news_count }}</div>
<div class="pej-stat-label">aktualności</div>
</div>
<div class="pej-stat">
<div class="pej-stat-value">{{ milestones_count }}</div>
<div class="pej-stat-label">kamieni milowych</div>
</div>
</div>
{% if announcements %}
<div class="pej-section">
<div class="pej-section-title">Komunikaty Izby</div>
{% for ann in announcements %}
<div class="pej-announcement">
<div class="pej-announcement-title">{{ ann.title }}</div>
<div class="pej-announcement-date">{{ ann.created_at.strftime('%d.%m.%Y') }}</div>
<p>{{ ann.content[:300] }}{% if ann.content|length > 300 %}...{% endif %}</p>
</div>
{% endfor %}
</div>
{% endif %}
<div class="pej-grid">
<div class="pej-section">
<div class="pej-section-title">Najnowsze aktualności</div>
{% if news %}
{% for item in news %}
<div class="pej-news-card">
<h3><a href="{{ item.url }}" target="_blank" rel="noopener">{{ item.title }}</a></h3>
<div class="meta">
{{ item.source_name or '' }}
{% if item.published_at %} &middot; {{ item.published_at.strftime('%d.%m.%Y') }}{% endif %}
</div>
</div>
{% endfor %}
<a href="{{ url_for('pej_news') }}" class="pej-btn pej-btn-outline" style="margin-top: var(--spacing-sm); display: inline-block;">Zobacz wszystkie &rarr;</a>
{% else %}
<div class="pej-empty">Brak aktualności nuklearnych.</div>
{% endif %}
</div>
<div class="pej-section">
<div class="pej-section-title">Kamienie milowe</div>
{% if milestones %}
{% for m in milestones %}
<div class="pej-milestone">
<div class="pej-milestone-date">
{% if m.target_date %}{{ m.target_date.strftime('%m/%Y') }}{% else %}&mdash;{% endif %}
</div>
<div>
<span class="pej-milestone-title">{{ m.title }}</span>
<span class="pej-milestone-status {{ m.status }}">{{ m.status }}</span>
</div>
</div>
{% endfor %}
{% else %}
<div class="pej-empty">Brak kamieni milowych.</div>
{% endif %}
</div>
</div>
<div class="pej-section">
<div class="pej-section-title">Local Content — Firmy z Izby gotowe do współpracy</div>
{% if top_companies %}
<div class="pej-company-grid">
{% for link, company in top_companies %}
<div class="pej-company-card">
<div class="pej-company-name">
<a href="{{ url_for('company_detail', slug=company.slug) }}">{{ company.name }}</a>
</div>
<div class="pej-company-meta">
{% if company.category %}<span>{{ company.category.name }}</span>{% endif %}
<span class="pej-company-score">{{ link.relevance_score }}/100</span>
{% if link.link_type %}
<span class="pej-company-type">{{ link_type_labels.get(link.link_type, link.link_type) }}</span>
{% endif %}
</div>
{% if link.collaboration_description %}
<div class="pej-company-desc">{{ link.collaboration_description[:150] }}{% if link.collaboration_description|length > 150 %}...{% endif %}</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="pej-cta">
<a href="{{ url_for('pej_local_content') }}" class="pej-btn pej-btn-primary">Zobacz pełną listę &rarr;</a>
{% if current_user.can_access_admin_panel() %}
<a href="{{ url_for('admin.pej_export_csv') }}" class="pej-btn pej-btn-outline">Eksportuj CSV</a>
{% endif %}
</div>
{% else %}
<div class="pej-empty">Brak dopasowanych firm.</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,159 @@
{% extends "base.html" %}
{% block title %}Local Content — Firmy dla PEJ - Norda Biznes Partner{% endblock %}
{% block extra_css %}
<style>
.lc-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: var(--spacing-lg); flex-wrap: wrap; gap: var(--spacing-md);
}
.lc-header h1 { font-size: 1.5rem; color: #7c3aed; }
.lc-export-btn {
display: inline-flex; align-items: center; gap: 6px;
padding: var(--spacing-sm) var(--spacing-md);
background: #7c3aed; color: #fff; border-radius: var(--radius);
text-decoration: none; font-size: var(--font-size-sm); font-weight: 500;
}
.lc-export-btn:hover { background: #6d28d9; }
.lc-filters {
display: flex; gap: var(--spacing-md); margin-bottom: var(--spacing-lg);
flex-wrap: wrap; align-items: center;
}
.lc-filters select, .lc-filters input {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--border); border-radius: var(--radius);
font-size: var(--font-size-sm);
}
.lc-filter-btn {
padding: var(--spacing-sm) var(--spacing-md);
background: #7c3aed; color: #fff; border: none;
border-radius: var(--radius); cursor: pointer; font-size: var(--font-size-sm);
}
.lc-filter-btn:hover { background: #6d28d9; }
.lc-count { font-size: var(--font-size-sm); color: var(--text-secondary); margin-bottom: var(--spacing-md); }
.lc-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: var(--spacing-md);
margin-bottom: var(--spacing-md); display: flex;
justify-content: space-between; align-items: flex-start;
gap: var(--spacing-md); transition: var(--transition);
}
.lc-card:hover { border-color: #7c3aed; }
.lc-card-main { flex: 1; }
.lc-card-name { font-weight: 600; font-size: var(--font-size-md); margin-bottom: var(--spacing-xs); }
.lc-card-name a { color: var(--text-primary); text-decoration: none; }
.lc-card-name a:hover { color: #7c3aed; }
.lc-card-meta { font-size: var(--font-size-sm); color: var(--text-secondary); margin-bottom: var(--spacing-xs); }
.lc-card-desc { font-size: var(--font-size-sm); color: var(--text-secondary); }
.lc-card-side { text-align: right; min-width: 100px; }
.lc-score {
font-size: 1.25rem; font-weight: 700; color: #7c3aed;
}
.lc-type {
display: inline-block; background: #e0e7ff; color: #3730a3;
padding: 2px 8px; border-radius: 10px; font-size: 0.75rem; margin-top: var(--spacing-xs);
}
.lc-pagination { display: flex; justify-content: center; gap: var(--spacing-sm); margin-top: var(--spacing-lg); }
.lc-pagination a {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--border); border-radius: var(--radius);
text-decoration: none; color: var(--text-primary); font-size: var(--font-size-sm);
}
.lc-pagination a:hover { border-color: #7c3aed; color: #7c3aed; }
.lc-pagination a.active { background: #7c3aed; color: #fff; border-color: #7c3aed; }
@media (max-width: 768px) {
.lc-card { flex-direction: column; }
.lc-card-side { text-align: left; }
}
</style>
{% endblock %}
{% block content %}
<div class="lc-header">
<div>
<h1>Local Content — Firmy Izby Norda dla PEJ</h1>
<a href="{{ url_for('pej_index') }}" style="font-size: var(--font-size-sm); color: var(--text-secondary);">&larr; Powrót do PEJ</a>
</div>
{% if current_user.can_access_admin_panel() %}
<a href="{{ url_for('admin.pej_export_csv') }}" class="lc-export-btn">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
Eksportuj CSV
</a>
{% endif %}
</div>
<form class="lc-filters" method="get">
<select name="category">
<option value="">Wszystkie branże</option>
{% for cat in categories %}
<option value="{{ cat.id }}" {% if cat.id == category_filter %}selected{% endif %}>{{ cat.name }}</option>
{% endfor %}
</select>
<select name="link_type">
<option value="">Wszystkie typy</option>
{% for lt in link_types %}
<option value="{{ lt }}" {% if lt == link_type_filter %}selected{% endif %}>{{ link_type_labels.get(lt, lt) }}</option>
{% endfor %}
</select>
<input type="text" name="q" placeholder="Szukaj firmy..." value="{{ search_query }}">
<button type="submit" class="lc-filter-btn">Filtruj</button>
{% if category_filter or link_type_filter or search_query %}
<a href="{{ url_for('pej_local_content') }}" style="font-size: var(--font-size-sm); color: var(--text-secondary);">Wyczyść filtry</a>
{% endif %}
</form>
<div class="lc-count">Znaleziono {{ total }} firm</div>
{% for link, company in results %}
<div class="lc-card">
<div class="lc-card-main">
<div class="lc-card-name">
<a href="{{ url_for('company_detail', slug=company.slug) }}">{{ company.name }}</a>
</div>
<div class="lc-card-meta">
{% if company.category %}{{ company.category.name }}{% endif %}
{% if company.address_city %} &middot; {{ company.address_city }}{% endif %}
{% if company.email %} &middot; {{ company.email }}{% endif %}
</div>
{% if link.collaboration_description %}
<div class="lc-card-desc">{{ link.collaboration_description }}</div>
{% endif %}
</div>
<div class="lc-card-side">
<div class="lc-score">{{ link.relevance_score }}<span style="font-size: 0.7em; color: var(--text-secondary);">/100</span></div>
{% if link.link_type %}
<div class="lc-type">{{ link_type_labels.get(link.link_type, link.link_type) }}</div>
{% endif %}
</div>
</div>
{% else %}
<div style="text-align: center; padding: var(--spacing-xl); color: var(--text-secondary);">
Brak firm spełniających kryteria.
</div>
{% endfor %}
{% if total_pages > 1 %}
<div class="lc-pagination">
{% if page > 1 %}
<a href="?page={{ page - 1 }}&category={{ category_filter }}&link_type={{ link_type_filter }}&q={{ search_query }}">&laquo; Poprzednia</a>
{% endif %}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<a class="active">{{ p }}</a>
{% elif p <= 3 or p > total_pages - 2 or (p >= page - 1 and p <= page + 1) %}
<a href="?page={{ p }}&category={{ category_filter }}&link_type={{ link_type_filter }}&q={{ search_query }}">{{ p }}</a>
{% elif p == 4 or p == total_pages - 2 %}
<span style="padding: var(--spacing-sm);">...</span>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<a href="?page={{ page + 1 }}&category={{ category_filter }}&link_type={{ link_type_filter }}&q={{ search_query }}">Następna &raquo;</a>
{% endif %}
</div>
{% endif %}
{% endblock %}

82
templates/pej/news.html Normal file
View File

@ -0,0 +1,82 @@
{% extends "base.html" %}
{% block title %}Aktualności PEJ - Norda Biznes Partner{% endblock %}
{% block extra_css %}
<style>
.pej-news-header {
margin-bottom: var(--spacing-lg);
}
.pej-news-header h1 { font-size: 1.5rem; color: #7c3aed; }
.pej-news-item {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: var(--spacing-md);
margin-bottom: var(--spacing-md); transition: var(--transition);
}
.pej-news-item:hover { border-color: #7c3aed; }
.pej-news-item h2 { font-size: var(--font-size-md); margin-bottom: var(--spacing-xs); }
.pej-news-item h2 a { color: var(--text-primary); text-decoration: none; }
.pej-news-item h2 a:hover { color: #7c3aed; }
.pej-news-item .meta {
font-size: var(--font-size-sm); color: var(--text-secondary);
margin-bottom: var(--spacing-sm);
}
.pej-news-item .summary { font-size: var(--font-size-sm); color: var(--text-secondary); }
.pej-pagination { display: flex; justify-content: center; gap: var(--spacing-sm); margin-top: var(--spacing-lg); }
.pej-pagination a {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--border); border-radius: var(--radius);
text-decoration: none; color: var(--text-primary); font-size: var(--font-size-sm);
}
.pej-pagination a:hover { border-color: #7c3aed; color: #7c3aed; }
.pej-pagination a.active { background: #7c3aed; color: #fff; border-color: #7c3aed; }
</style>
{% endblock %}
{% block content %}
<div class="pej-news-header">
<h1>Aktualności — Elektrownia Jądrowa</h1>
<a href="{{ url_for('pej_index') }}" style="font-size: var(--font-size-sm); color: var(--text-secondary);">&larr; Powrót do PEJ</a>
<p style="font-size: var(--font-size-sm); color: var(--text-secondary); margin-top: var(--spacing-xs);">{{ total }} artykułów</p>
</div>
{% for item in news %}
<div class="pej-news-item">
<h2><a href="{{ item.url }}" target="_blank" rel="noopener">{{ item.title }}</a></h2>
<div class="meta">
{{ item.source_name or 'Źródło nieznane' }}
{% if item.published_at %} &middot; {{ item.published_at.strftime('%d.%m.%Y') }}{% endif %}
{% if item.ai_relevance_score %}
&middot; <span title="Trafność AI">{% for i in range(item.ai_relevance_score) %}&#9733;{% endfor %}{% for i in range(5 - item.ai_relevance_score) %}&#9734;{% endfor %}</span>
{% endif %}
</div>
{% if item.summary %}
<div class="summary">{{ item.summary[:200] }}{% if item.summary|length > 200 %}...{% endif %}</div>
{% endif %}
</div>
{% else %}
<div style="text-align: center; padding: var(--spacing-xl); color: var(--text-secondary);">
Brak aktualności nuklearnych.
</div>
{% endfor %}
{% if total_pages > 1 %}
<div class="pej-pagination">
{% if page > 1 %}
<a href="?page={{ page - 1 }}">&laquo; Poprzednia</a>
{% endif %}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<a class="active">{{ p }}</a>
{% elif p <= 3 or p > total_pages - 2 or (p >= page - 1 and p <= page + 1) %}
<a href="?page={{ p }}">{{ p }}</a>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<a href="?page={{ page + 1 }}">Następna &raquo;</a>
{% endif %}
</div>
{% endif %}
{% endblock %}