feat(staging): Add visual test indicators for staging environment
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
Adds staging-only UI elements: environment banner, TEST badges on nav items, and floating test panel with feature checklist. Controlled by STAGING=true env var — zero impact on production. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f318956429
commit
09ef2b62a5
16
app.py
16
app.py
@ -51,6 +51,19 @@ else:
|
||||
# Używana we wszystkich miejscach wyświetlających liczbę firm
|
||||
COMPANY_COUNT_MARKETING = 150
|
||||
|
||||
# ============================================================
|
||||
# STAGING TEST FEATURES
|
||||
# ============================================================
|
||||
# Features currently being tested on staging environment.
|
||||
# Only rendered when STAGING=true in .env. Edit this dict to update.
|
||||
STAGING_TEST_FEATURES = {
|
||||
'board_module': {
|
||||
'name': 'Moduł Rada Izby',
|
||||
'description': 'Zarządzanie posiedzeniami, dokumenty, rola OFFICE_MANAGER',
|
||||
'nav_item': 'Rada',
|
||||
},
|
||||
}
|
||||
|
||||
# Configure logging with in-memory buffer for debug panel
|
||||
class DebugLogHandler(logging.Handler):
|
||||
"""Custom handler that stores logs in memory for real-time viewing"""
|
||||
@ -319,10 +332,13 @@ def load_user(user_id):
|
||||
@app.context_processor
|
||||
def inject_globals():
|
||||
"""Inject global variables into all templates"""
|
||||
is_staging = os.getenv('STAGING') == 'true'
|
||||
return {
|
||||
'current_year': datetime.now().year,
|
||||
'now': datetime.now(), # Must be value, not method - templates use now.strftime()
|
||||
'COMPANY_COUNT': COMPANY_COUNT_MARKETING, # Liczba podmiotów (cel marketingowy)
|
||||
'is_staging': is_staging,
|
||||
'staging_features': STAGING_TEST_FEATURES if is_staging else {},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1063,6 +1063,158 @@
|
||||
}
|
||||
}
|
||||
|
||||
{% if is_staging %}
|
||||
/* ============================================================
|
||||
* STAGING ENVIRONMENT INDICATORS
|
||||
* ============================================================ */
|
||||
.staging-banner {
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 6px var(--spacing-md);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
z-index: 1000;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-test-badge {
|
||||
display: inline-block;
|
||||
background: #f97316;
|
||||
color: white;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
animation: staging-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes staging-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.staging-panel-toggle {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.staging-panel-toggle:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.staging-panel-toggle svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.staging-panel {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
width: 320px;
|
||||
max-height: 400px;
|
||||
background: var(--surface);
|
||||
border: 2px solid #f97316;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 9998;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.staging-panel.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.staging-panel-header {
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.staging-panel-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.staging-panel-body {
|
||||
padding: 12px 16px;
|
||||
overflow-y: auto;
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
.staging-feature-item {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.staging-feature-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.staging-feature-name {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.staging-feature-status {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #f97316;
|
||||
display: inline-block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.staging-feature-desc {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.staging-panel {
|
||||
width: calc(100vw - 40px);
|
||||
right: 20px;
|
||||
bottom: 76px;
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
</style>
|
||||
|
||||
@ -1070,6 +1222,11 @@
|
||||
<script src="{{ url_for('static', filename='js/analytics-tracker.js') }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
{% if is_staging %}
|
||||
<!-- Staging Environment Banner -->
|
||||
<div class="staging-banner">⚠ STAGING — Środowisko testowe</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Header -->
|
||||
<header>
|
||||
<div class="container">
|
||||
@ -1820,5 +1977,58 @@
|
||||
|
||||
<!-- Connections Map Modal -->
|
||||
{% include 'connections_modal.html' %}
|
||||
|
||||
{% if is_staging %}
|
||||
<!-- Staging Test Panel -->
|
||||
<button type="button" class="staging-panel-toggle" onclick="toggleStagingPanel()" title="Panel testowy">
|
||||
<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-6 9l2 2 4-4"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="staging-panel" id="stagingPanel">
|
||||
<div class="staging-panel-header">
|
||||
<span>Panel testowy</span>
|
||||
<button type="button" class="staging-panel-close" onclick="toggleStagingPanel()" title="Zamknij panel">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" width="16" height="16">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="staging-panel-body">
|
||||
{% for key, feature in staging_features.items() %}
|
||||
<div class="staging-feature-item">
|
||||
<div class="staging-feature-name">
|
||||
<span class="staging-feature-status"></span>
|
||||
{{ feature.name }}
|
||||
</div>
|
||||
<div class="staging-feature-desc">{{ feature.description }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function toggleStagingPanel() {
|
||||
document.getElementById('stagingPanel').classList.toggle('open');
|
||||
}
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
var panel = document.getElementById('stagingPanel');
|
||||
if (panel) panel.classList.remove('open');
|
||||
}
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var testNavItems = {{ staging_features.values()|map(attribute='nav_item')|select('defined')|list|tojson }};
|
||||
document.querySelectorAll('.nav-menu .nav-link, .admin-bar a').forEach(function(link) {
|
||||
var text = link.textContent.trim();
|
||||
if (testNavItems.indexOf(text) !== -1) {
|
||||
var badge = document.createElement('span');
|
||||
badge.className = 'nav-test-badge';
|
||||
badge.textContent = 'TEST';
|
||||
link.appendChild(badge);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user