feat: add Roadmap admin page with kanban-style board
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
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
New /admin/roadmap page showing feature requests from members in three columns: Planned, In Progress, Done. Cards expand on click to show implementation details. First item: multi-location support requested by Daniel Kochański (Stalpunkt). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
776e84b3dc
commit
528bd3d727
@ -37,3 +37,4 @@ from . import routes_portal_seo # noqa: E402, F401
|
||||
from . import routes_user_insights # noqa: E402, F401
|
||||
from . import routes_user_activity # noqa: E402, F401
|
||||
from . import routes_company_wizard # noqa: E402, F401
|
||||
from . import routes_roadmap # noqa: E402, F401
|
||||
|
||||
75
blueprints/admin/routes_roadmap.py
Normal file
75
blueprints/admin/routes_roadmap.py
Normal file
@ -0,0 +1,75 @@
|
||||
"""
|
||||
Admin Roadmap Routes
|
||||
====================
|
||||
|
||||
Product roadmap — feature requests from members and strategic plans.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from flask import render_template
|
||||
from flask_login import login_required
|
||||
from . import bp
|
||||
from database import SystemRole
|
||||
from utils.decorators import role_required
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
ROADMAP_ITEMS = [
|
||||
{
|
||||
'id': 1,
|
||||
'title': 'Wiele lokalizacji firmy na mapie',
|
||||
'description': 'Firmy z kilkoma oddziałami/punktami mogą dodać wiele adresów. '
|
||||
'Każdy wyświetlany na profilu z linkiem do Google Maps.',
|
||||
'details': [
|
||||
'Nowa tabela company_locations (label, adres, lat/lng na przyszłość)',
|
||||
'Edycja w zakładce Kontakt w profilu firmy (wzorzec jak CompanyWebsite)',
|
||||
'Dynamiczne wiersze: dodaj/usuń lokalizację, max 10',
|
||||
'Wyświetlanie na profilu firmy z linkami "Pokaż na mapie"',
|
||||
],
|
||||
'requested_by': 'Daniel Kochański (Stalpunkt)',
|
||||
'date': '2026-04-09',
|
||||
'priority': 'medium',
|
||||
'status': 'planned',
|
||||
'category': 'feature',
|
||||
},
|
||||
]
|
||||
|
||||
STATUS_LABELS = {
|
||||
'planned': ('Planowane', '#6366f1'),
|
||||
'in_progress': ('W trakcie', '#f59e0b'),
|
||||
'done': ('Zrealizowane', '#10b981'),
|
||||
'on_hold': ('Wstrzymane', '#94a3b8'),
|
||||
}
|
||||
|
||||
PRIORITY_LABELS = {
|
||||
'high': ('Wysoki', '#ef4444'),
|
||||
'medium': ('Średni', '#f59e0b'),
|
||||
'low': ('Niski', '#6b7280'),
|
||||
}
|
||||
|
||||
CATEGORY_LABELS = {
|
||||
'feature': 'Nowa funkcjonalność',
|
||||
'improvement': 'Ulepszenie',
|
||||
'bugfix': 'Naprawa błędu',
|
||||
}
|
||||
|
||||
|
||||
@bp.route('/roadmap')
|
||||
@login_required
|
||||
@role_required(SystemRole.ADMIN)
|
||||
def admin_roadmap():
|
||||
"""Product roadmap — feature requests and development plans."""
|
||||
planned = [i for i in ROADMAP_ITEMS if i['status'] == 'planned']
|
||||
in_progress = [i for i in ROADMAP_ITEMS if i['status'] == 'in_progress']
|
||||
done = [i for i in ROADMAP_ITEMS if i['status'] == 'done']
|
||||
|
||||
return render_template(
|
||||
'admin/roadmap.html',
|
||||
planned=planned,
|
||||
in_progress=in_progress,
|
||||
done=done,
|
||||
status_labels=STATUS_LABELS,
|
||||
priority_labels=PRIORITY_LABELS,
|
||||
category_labels=CATEGORY_LABELS,
|
||||
)
|
||||
@ -86,9 +86,17 @@ Najwyższy poziom (Enterprise) służy jako **kotwica cenowa** — sprawia że P
|
||||
| Projekt Kaszubia — dostęp | - | **+** | + |
|
||||
| Konta pracowników z rolami | - | - | **+** |
|
||||
|
||||
### Roadmapa rozwoju (ze slajdu 9, prezentacja Rada 13.02.2026)
|
||||
### Roadmapa rozwoju
|
||||
|
||||
Funkcje planowane do wdrożenia, wspólnie decydowane z Radą NORDA:
|
||||
#### Zgłoszenia od członków (backlog)
|
||||
|
||||
| # | Funkcjonalność | Opis | Zgłosił | Data | Priorytet | Status |
|
||||
|---|---------------|------|---------|------|-----------|--------|
|
||||
| 1 | Wiele lokalizacji firmy na mapie | Firmy z kilkoma oddziałami/punktami mogą dodać wiele adresów. Każdy wyświetlany na profilu z linkiem do Google Maps. Nowa tabela `company_locations` (label, adres, lat/lng na przyszłość), edycja w zakładce Kontakt w profilu firmy, wzorzec jak CompanyWebsite (dynamiczne wiersze, delete-and-reinsert). | Daniel Kochański (Stalpunkt) | 2026-04-09 | Średni | Planowane |
|
||||
|
||||
#### Funkcje strategiczne (ze slajdu 9, prezentacja Rada 13.02.2026)
|
||||
|
||||
Wspólnie decydowane z Radą NORDA:
|
||||
|
||||
| # | Funkcjonalność | Opis |
|
||||
|---|---------------|------|
|
||||
|
||||
129
templates/admin/roadmap.html
Normal file
129
templates/admin/roadmap.html
Normal file
@ -0,0 +1,129 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Roadmapa - Admin - Norda Biznes Partner{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
.roadmap-header { margin-bottom: var(--spacing-xl); }
|
||||
.roadmap-header h1 { font-size: var(--font-size-2xl); font-weight: 700; color: var(--text-primary); margin-bottom: var(--spacing-xs); }
|
||||
.roadmap-header p { color: var(--text-secondary); font-size: var(--font-size-sm); }
|
||||
|
||||
.roadmap-columns { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-lg); }
|
||||
@media (max-width: 1024px) { .roadmap-columns { grid-template-columns: 1fr; } }
|
||||
|
||||
.roadmap-column { background: var(--surface); border-radius: var(--radius-lg); padding: var(--spacing-lg); border: 1px solid var(--border); }
|
||||
.roadmap-column-header { display: flex; align-items: center; gap: var(--spacing-sm); margin-bottom: var(--spacing-lg); padding-bottom: var(--spacing-md); border-bottom: 2px solid var(--border); }
|
||||
.roadmap-column-header h2 { font-size: var(--font-size-lg); font-weight: 600; }
|
||||
.roadmap-column-count { background: var(--background); color: var(--text-secondary); font-size: var(--font-size-sm); font-weight: 600; padding: 2px 8px; border-radius: 10px; }
|
||||
|
||||
.roadmap-card { background: var(--background); border-radius: var(--radius); padding: var(--spacing-md); margin-bottom: var(--spacing-md); border: 1px solid var(--border); transition: var(--transition); cursor: pointer; }
|
||||
.roadmap-card:hover { border-color: var(--primary-light); box-shadow: var(--shadow-md); }
|
||||
.roadmap-card:last-child { margin-bottom: 0; }
|
||||
.roadmap-card-title { font-weight: 600; font-size: var(--font-size-base); color: var(--text-primary); margin-bottom: var(--spacing-sm); }
|
||||
.roadmap-card-desc { font-size: var(--font-size-sm); color: var(--text-secondary); line-height: 1.5; margin-bottom: var(--spacing-md); }
|
||||
.roadmap-card-meta { display: flex; flex-wrap: wrap; gap: var(--spacing-sm); align-items: center; font-size: 12px; }
|
||||
.roadmap-badge { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 4px; font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.3px; }
|
||||
.roadmap-card-requester { color: var(--text-secondary); font-size: 12px; }
|
||||
.roadmap-card-date { color: var(--text-secondary); font-size: 12px; }
|
||||
|
||||
.roadmap-details { display: none; margin-top: var(--spacing-md); padding-top: var(--spacing-md); border-top: 1px solid var(--border); }
|
||||
.roadmap-details.open { display: block; }
|
||||
.roadmap-details ul { list-style: none; padding: 0; }
|
||||
.roadmap-details li { font-size: var(--font-size-sm); color: var(--text-secondary); padding: 4px 0 4px 16px; position: relative; }
|
||||
.roadmap-details li::before { content: "→"; position: absolute; left: 0; color: var(--primary); }
|
||||
|
||||
.roadmap-empty { text-align: center; padding: var(--spacing-xl); color: var(--text-secondary); font-size: var(--font-size-sm); }
|
||||
|
||||
.col-planned .roadmap-column-header { border-bottom-color: #6366f1; }
|
||||
.col-progress .roadmap-column-header { border-bottom-color: #f59e0b; }
|
||||
.col-done .roadmap-column-header { border-bottom-color: #10b981; }
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="roadmap-header">
|
||||
<h1>Roadmapa rozwoju</h1>
|
||||
<p>Zgłoszenia od członków i planowane funkcjonalności platformy</p>
|
||||
</div>
|
||||
|
||||
<div class="roadmap-columns">
|
||||
<div class="roadmap-column col-planned">
|
||||
<div class="roadmap-column-header">
|
||||
<h2>Planowane</h2>
|
||||
<span class="roadmap-column-count">{{ planned|length }}</span>
|
||||
</div>
|
||||
{% for item in planned %}
|
||||
<div class="roadmap-card" onclick="this.querySelector('.roadmap-details').classList.toggle('open')">
|
||||
<div class="roadmap-card-title">{{ item.title }}</div>
|
||||
<div class="roadmap-card-desc">{{ item.description }}</div>
|
||||
<div class="roadmap-card-meta">
|
||||
<span class="roadmap-badge" style="background: {{ priority_labels[item.priority][1] }}20; color: {{ priority_labels[item.priority][1] }};">{{ priority_labels[item.priority][0] }}</span>
|
||||
<span class="roadmap-badge" style="background: var(--primary-light); background: #e0e7ff; color: #4338ca;">{{ category_labels.get(item.category, item.category) }}</span>
|
||||
<span class="roadmap-card-date">{{ item.date }}</span>
|
||||
</div>
|
||||
<div class="roadmap-card-requester">Zgłosił: {{ item.requested_by }}</div>
|
||||
{% if item.details %}
|
||||
<div class="roadmap-details">
|
||||
<ul>
|
||||
{% for detail in item.details %}
|
||||
<li>{{ detail }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="roadmap-empty">Brak zaplanowanych zadań</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="roadmap-column col-progress">
|
||||
<div class="roadmap-column-header">
|
||||
<h2>W trakcie</h2>
|
||||
<span class="roadmap-column-count">{{ in_progress|length }}</span>
|
||||
</div>
|
||||
{% for item in in_progress %}
|
||||
<div class="roadmap-card" onclick="this.querySelector('.roadmap-details').classList.toggle('open')">
|
||||
<div class="roadmap-card-title">{{ item.title }}</div>
|
||||
<div class="roadmap-card-desc">{{ item.description }}</div>
|
||||
<div class="roadmap-card-meta">
|
||||
<span class="roadmap-badge" style="background: {{ priority_labels[item.priority][1] }}20; color: {{ priority_labels[item.priority][1] }};">{{ priority_labels[item.priority][0] }}</span>
|
||||
<span class="roadmap-card-date">{{ item.date }}</span>
|
||||
</div>
|
||||
<div class="roadmap-card-requester">Zgłosił: {{ item.requested_by }}</div>
|
||||
{% if item.details %}
|
||||
<div class="roadmap-details">
|
||||
<ul>
|
||||
{% for detail in item.details %}
|
||||
<li>{{ detail }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="roadmap-empty">Brak zadań w realizacji</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="roadmap-column col-done">
|
||||
<div class="roadmap-column-header">
|
||||
<h2>Zrealizowane</h2>
|
||||
<span class="roadmap-column-count">{{ done|length }}</span>
|
||||
</div>
|
||||
{% for item in done %}
|
||||
<div class="roadmap-card" onclick="this.querySelector('.roadmap-details').classList.toggle('open')">
|
||||
<div class="roadmap-card-title">{{ item.title }}</div>
|
||||
<div class="roadmap-card-desc">{{ item.description }}</div>
|
||||
<div class="roadmap-card-meta">
|
||||
<span class="roadmap-badge" style="background: #d1fae5; color: #065f46;">{{ category_labels.get(item.category, item.category) }}</span>
|
||||
<span class="roadmap-card-date">{{ item.date }}</span>
|
||||
</div>
|
||||
<div class="roadmap-card-requester">Zgłosił: {{ item.requested_by }}</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="roadmap-empty">Brak zrealizowanych zadań</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -487,6 +487,12 @@
|
||||
</svg>
|
||||
Aktywność użytkowników
|
||||
</a>
|
||||
<a href="{{ url_for('admin.admin_roadmap') }}">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/>
|
||||
</svg>
|
||||
Roadmapa
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user