htmlgraph 0.24.1__py3-none-any.whl → 0.25.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htmlgraph/__init__.py +20 -1
- htmlgraph/agent_detection.py +26 -10
- htmlgraph/analytics/cross_session.py +4 -3
- htmlgraph/analytics/work_type.py +52 -16
- htmlgraph/analytics_index.py +51 -19
- htmlgraph/api/__init__.py +3 -0
- htmlgraph/api/main.py +2115 -0
- htmlgraph/api/static/htmx.min.js +1 -0
- htmlgraph/api/static/style-redesign.css +1344 -0
- htmlgraph/api/static/style.css +1079 -0
- htmlgraph/api/templates/dashboard-redesign.html +812 -0
- htmlgraph/api/templates/dashboard.html +783 -0
- htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
- htmlgraph/api/templates/partials/activity-feed.html +570 -0
- htmlgraph/api/templates/partials/agents-redesign.html +317 -0
- htmlgraph/api/templates/partials/agents.html +317 -0
- htmlgraph/api/templates/partials/event-traces.html +373 -0
- htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
- htmlgraph/api/templates/partials/features.html +509 -0
- htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
- htmlgraph/api/templates/partials/metrics.html +346 -0
- htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
- htmlgraph/api/templates/partials/orchestration.html +163 -0
- htmlgraph/api/templates/partials/spawners.html +375 -0
- htmlgraph/atomic_ops.py +560 -0
- htmlgraph/builders/base.py +55 -1
- htmlgraph/builders/bug.py +17 -2
- htmlgraph/builders/chore.py +17 -2
- htmlgraph/builders/epic.py +17 -2
- htmlgraph/builders/feature.py +25 -2
- htmlgraph/builders/phase.py +17 -2
- htmlgraph/builders/spike.py +27 -2
- htmlgraph/builders/track.py +14 -0
- htmlgraph/cigs/__init__.py +4 -0
- htmlgraph/cigs/reporter.py +818 -0
- htmlgraph/cli.py +1427 -401
- htmlgraph/cli_commands/__init__.py +1 -0
- htmlgraph/cli_commands/feature.py +195 -0
- htmlgraph/cli_framework.py +115 -0
- htmlgraph/collections/__init__.py +2 -0
- htmlgraph/collections/base.py +21 -0
- htmlgraph/collections/session.py +189 -0
- htmlgraph/collections/spike.py +7 -1
- htmlgraph/collections/task_delegation.py +236 -0
- htmlgraph/collections/traces.py +482 -0
- htmlgraph/config.py +113 -0
- htmlgraph/converter.py +41 -0
- htmlgraph/cost_analysis/__init__.py +5 -0
- htmlgraph/cost_analysis/analyzer.py +438 -0
- htmlgraph/dashboard.html +3315 -492
- htmlgraph-0.24.1.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
- htmlgraph/dashboard.html.bak +7181 -0
- htmlgraph/dashboard.html.bak2 +7231 -0
- htmlgraph/dashboard.html.bak3 +7232 -0
- htmlgraph/db/__init__.py +38 -0
- htmlgraph/db/queries.py +790 -0
- htmlgraph/db/schema.py +1334 -0
- htmlgraph/deploy.py +26 -27
- htmlgraph/docs/API_REFERENCE.md +841 -0
- htmlgraph/docs/HTTP_API.md +750 -0
- htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
- htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
- htmlgraph/docs/README.md +533 -0
- htmlgraph/docs/version_check.py +3 -1
- htmlgraph/error_handler.py +544 -0
- htmlgraph/event_log.py +2 -0
- htmlgraph/hooks/__init__.py +8 -0
- htmlgraph/hooks/bootstrap.py +169 -0
- htmlgraph/hooks/context.py +271 -0
- htmlgraph/hooks/drift_handler.py +521 -0
- htmlgraph/hooks/event_tracker.py +405 -15
- htmlgraph/hooks/post_tool_use_handler.py +257 -0
- htmlgraph/hooks/pretooluse.py +476 -6
- htmlgraph/hooks/prompt_analyzer.py +648 -0
- htmlgraph/hooks/session_handler.py +583 -0
- htmlgraph/hooks/state_manager.py +501 -0
- htmlgraph/hooks/subagent_stop.py +309 -0
- htmlgraph/hooks/task_enforcer.py +39 -0
- htmlgraph/models.py +111 -15
- htmlgraph/operations/fastapi_server.py +230 -0
- htmlgraph/orchestration/headless_spawner.py +22 -14
- htmlgraph/pydantic_models.py +476 -0
- htmlgraph/quality_gates.py +350 -0
- htmlgraph/repo_hash.py +511 -0
- htmlgraph/sdk.py +348 -10
- htmlgraph/server.py +194 -0
- htmlgraph/session_hooks.py +300 -0
- htmlgraph/session_manager.py +131 -1
- htmlgraph/session_registry.py +587 -0
- htmlgraph/session_state.py +436 -0
- htmlgraph/system_prompts.py +449 -0
- htmlgraph/templates/orchestration-view.html +350 -0
- htmlgraph/track_builder.py +19 -0
- htmlgraph/validation.py +115 -0
- htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
- {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
- {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
<div class="view-container metrics-view">
|
|
2
|
+
<div class="view-header">
|
|
3
|
+
<h2>Performance Metrics & Analytics</h2>
|
|
4
|
+
<div class="view-description">
|
|
5
|
+
Real-time system performance, token usage, and agent statistics
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<!-- REAL-TIME STATS SECTION -->
|
|
10
|
+
<div class="metrics-section">
|
|
11
|
+
<h3>Real-Time System Stats</h3>
|
|
12
|
+
<div class="metrics-grid">
|
|
13
|
+
<div class="metric-card">
|
|
14
|
+
<div class="metric-label">Events Per Minute</div>
|
|
15
|
+
<div class="metric-value" id="events-per-minute">0</div>
|
|
16
|
+
<div class="metric-trend">Current rate</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="metric-card">
|
|
19
|
+
<div class="metric-label">Avg Token Cost</div>
|
|
20
|
+
<div class="metric-value" id="avg-token-cost">0</div>
|
|
21
|
+
<div class="metric-trend">Per event</div>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="metric-card">
|
|
24
|
+
<div class="metric-label">Active Sessions</div>
|
|
25
|
+
<div class="metric-value" id="active-sessions">{{ active_sessions or 0 }}</div>
|
|
26
|
+
<div class="metric-trend">Concurrent</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="metric-card">
|
|
29
|
+
<div class="metric-label">System Status</div>
|
|
30
|
+
<div class="metric-value" id="system-status" style="font-size: 1.8rem; color: var(--status-success);">●</div>
|
|
31
|
+
<div class="metric-trend">Operational</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- TOKEN USAGE SECTION -->
|
|
37
|
+
{% if token_stats %}
|
|
38
|
+
<div class="metrics-section">
|
|
39
|
+
<h3>Token Usage Breakdown</h3>
|
|
40
|
+
<div class="metrics-grid">
|
|
41
|
+
<div class="metric-card">
|
|
42
|
+
<div class="metric-label">Total Tokens</div>
|
|
43
|
+
<div class="metric-value">{{ "{:,}".format(token_stats.total_tokens) }}</div>
|
|
44
|
+
<div class="metric-trend">All time</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="metric-card">
|
|
47
|
+
<div class="metric-label">Avg Per Event</div>
|
|
48
|
+
<div class="metric-value">{{ "{:,}".format(token_stats.avg_per_event) }}</div>
|
|
49
|
+
<div class="metric-trend">Current session</div>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="metric-card">
|
|
52
|
+
<div class="metric-label">Peak Usage</div>
|
|
53
|
+
<div class="metric-value">{{ "{:,}".format(token_stats.peak_usage) }}</div>
|
|
54
|
+
<div class="metric-trend">Single event</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="metric-card">
|
|
57
|
+
<div class="metric-label">Cost Estimate</div>
|
|
58
|
+
<div class="metric-value">{{ "$%.2f"|format(token_stats.estimated_cost) }}</div>
|
|
59
|
+
<div class="metric-trend">USD (est.)</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
{% endif %}
|
|
64
|
+
|
|
65
|
+
<!-- ACTIVITY TIMELINE SECTION -->
|
|
66
|
+
<div class="metrics-section">
|
|
67
|
+
<h3>Event Activity Timeline (Last 24h)</h3>
|
|
68
|
+
<div class="chart-container">
|
|
69
|
+
{% if activity_timeline %}
|
|
70
|
+
{% for hour, count in activity_timeline.items() %}
|
|
71
|
+
<div style="display: flex; flex-direction: column; align-items: center; flex: 1;">
|
|
72
|
+
<div class="chart-value">{{ count }}</div>
|
|
73
|
+
<div class="chart-bar" style="height: {% if count > 0 %}{{ (count / max_hourly_count * 100)|int }}%{% else %}5{% endif %}; min-height: 5px;">
|
|
74
|
+
</div>
|
|
75
|
+
<div class="chart-label">{{ hour }}h</div>
|
|
76
|
+
</div>
|
|
77
|
+
{% endfor %}
|
|
78
|
+
{% else %}
|
|
79
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-muted);">
|
|
80
|
+
<p>No activity data available yet</p>
|
|
81
|
+
</div>
|
|
82
|
+
{% endif %}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- AGENT PERFORMANCE SECTION -->
|
|
87
|
+
{% if agent_performance %}
|
|
88
|
+
<div class="metrics-section">
|
|
89
|
+
<h3>Agent Performance Ranking</h3>
|
|
90
|
+
<div style="background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: 2px; overflow: hidden;">
|
|
91
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
92
|
+
<thead style="background: var(--bg-darker); border-bottom: 1px solid var(--border-subtle);">
|
|
93
|
+
<tr>
|
|
94
|
+
<th style="padding: var(--spacing-lg); text-align: left; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Agent</th>
|
|
95
|
+
<th style="padding: var(--spacing-lg); text-align: right; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Events</th>
|
|
96
|
+
<th style="padding: var(--spacing-lg); text-align: right; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Tokens</th>
|
|
97
|
+
<th style="padding: var(--spacing-lg); text-align: right; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Success Rate</th>
|
|
98
|
+
<th style="padding: var(--spacing-lg); text-align: right; color: var(--accent-lime); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;">Avg Time</th>
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{% for agent in agent_performance %}
|
|
103
|
+
<tr style="border-bottom: 1px solid var(--border-subtle);">
|
|
104
|
+
<td style="padding: var(--spacing-lg); color: var(--text-primary); font-weight: 600;">
|
|
105
|
+
<span style="display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: var(--spacing-md);"
|
|
106
|
+
style="background: {% if agent.model == 'claude' %}var(--agent-claude){% elif agent.model == 'gemini' %}var(--agent-gemini){% else %}var(--agent-copilot){% endif %};"></span>
|
|
107
|
+
{{ agent.name or agent.id }}
|
|
108
|
+
</td>
|
|
109
|
+
<td style="padding: var(--spacing-lg); text-align: right; color: var(--text-secondary);">{{ agent.event_count }}</td>
|
|
110
|
+
<td style="padding: var(--spacing-lg); text-align: right; color: var(--accent-lime); font-weight: 600;">{{ "{:,}".format(agent.total_tokens) }}</td>
|
|
111
|
+
<td style="padding: var(--spacing-lg); text-align: right;">
|
|
112
|
+
<span style="color: {% if agent.success_rate >= 90 %}var(--status-success){% elif agent.success_rate >= 70 %}#FBBF24{% else %}var(--status-blocked){% endif %};">
|
|
113
|
+
{{ agent.success_rate }}%
|
|
114
|
+
</span>
|
|
115
|
+
</td>
|
|
116
|
+
<td style="padding: var(--spacing-lg); text-align: right; color: var(--text-secondary);">{{ "%.2f"|format(agent.avg_duration) }}s</td>
|
|
117
|
+
</tr>
|
|
118
|
+
{% endfor %}
|
|
119
|
+
</tbody>
|
|
120
|
+
</table>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
{% endif %}
|
|
124
|
+
|
|
125
|
+
<!-- EXECUTION TIME DISTRIBUTION SECTION -->
|
|
126
|
+
<div class="metrics-section">
|
|
127
|
+
<h3>Execution Time Distribution</h3>
|
|
128
|
+
<div class="chart-container">
|
|
129
|
+
<div style="flex: 1; text-align: center;">
|
|
130
|
+
<div class="chart-value" style="font-size: 1.4rem;">< 1s</div>
|
|
131
|
+
<div class="chart-bar" style="height: 40%;"></div>
|
|
132
|
+
<div class="chart-label">{{ "{:,}".format(exec_time_dist.very_fast or 0) }}</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div style="flex: 1; text-align: center;">
|
|
135
|
+
<div class="chart-value" style="font-size: 1.4rem;">1-5s</div>
|
|
136
|
+
<div class="chart-bar" style="height: 65%;"></div>
|
|
137
|
+
<div class="chart-label">{{ "{:,}".format(exec_time_dist.fast or 0) }}</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div style="flex: 1; text-align: center;">
|
|
140
|
+
<div class="chart-value" style="font-size: 1.4rem;">5-10s</div>
|
|
141
|
+
<div class="chart-bar" style="height: 85%;"></div>
|
|
142
|
+
<div class="chart-label">{{ "{:,}".format(exec_time_dist.medium or 0) }}</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div style="flex: 1; text-align: center;">
|
|
145
|
+
<div class="chart-value" style="font-size: 1.4rem;">10-30s</div>
|
|
146
|
+
<div class="chart-bar" style="height: 55%;"></div>
|
|
147
|
+
<div class="chart-label">{{ "{:,}".format(exec_time_dist.slow or 0) }}</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div style="flex: 1; text-align: center;">
|
|
150
|
+
<div class="chart-value" style="font-size: 1.4rem;">> 30s</div>
|
|
151
|
+
<div class="chart-bar" style="height: 20%;"></div>
|
|
152
|
+
<div class="chart-label">{{ "{:,}".format(exec_time_dist.very_slow or 0) }}</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<!-- SYSTEM HEALTH SECTION -->
|
|
158
|
+
<div class="metrics-section">
|
|
159
|
+
<h3>System Health Indicators</h3>
|
|
160
|
+
<div class="metrics-grid">
|
|
161
|
+
<div class="metric-card">
|
|
162
|
+
<div class="metric-label">Error Rate</div>
|
|
163
|
+
<div class="metric-value" style="{% if error_rate <= 5 %}color: var(--status-success){% elif error_rate <= 10 %}color: #FBBF24{% else %}color: var(--status-blocked){% endif %};">
|
|
164
|
+
{{ error_rate }}%
|
|
165
|
+
</div>
|
|
166
|
+
<div class="metric-trend">
|
|
167
|
+
{% if error_rate <= 5 %}Excellent{% elif error_rate <= 10 %}Good{% else %}Poor{% endif %}
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="metric-card">
|
|
171
|
+
<div class="metric-label">WebSocket Uptime</div>
|
|
172
|
+
<div class="metric-value" style="color: var(--status-success);">
|
|
173
|
+
99.9%
|
|
174
|
+
</div>
|
|
175
|
+
<div class="metric-trend">Last 24h</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="metric-card">
|
|
178
|
+
<div class="metric-label">Avg Response Time</div>
|
|
179
|
+
<div class="metric-value">
|
|
180
|
+
{{ "%.0f"|format(avg_response_time * 1000) }}ms
|
|
181
|
+
</div>
|
|
182
|
+
<div class="metric-trend">
|
|
183
|
+
{% if avg_response_time < 1 %}Excellent{% elif avg_response_time < 3 %}Good{% else %}Needs optimization{% endif %}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
<div class="metric-card">
|
|
187
|
+
<div class="metric-label">Database Load</div>
|
|
188
|
+
<div class="metric-value" style="font-size: 1.8rem;">
|
|
189
|
+
<div style="width: 100px; height: 6px; background: var(--bg-darker); border-radius: 2px; overflow: hidden;">
|
|
190
|
+
<div style="height: 100%; background: linear-gradient(90deg, var(--status-success) 0%, var(--accent-lime) 100%); width: 35%;"></div>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="metric-trend">35% utilization</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<script>
|
|
200
|
+
// Update real-time metrics
|
|
201
|
+
function updateRealtimeMetrics() {
|
|
202
|
+
// This would be connected to WebSocket updates in production
|
|
203
|
+
const epm = Math.floor(Math.random() * 50) + 10; // 10-60 events/min
|
|
204
|
+
document.getElementById('events-per-minute').textContent = epm;
|
|
205
|
+
|
|
206
|
+
const avgCost = Math.floor(Math.random() * 500) + 100; // 100-600 tokens
|
|
207
|
+
document.getElementById('avg-token-cost').textContent = avgCost.toLocaleString();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Update on page load
|
|
211
|
+
updateRealtimeMetrics();
|
|
212
|
+
|
|
213
|
+
// Update every 10 seconds
|
|
214
|
+
setInterval(updateRealtimeMetrics, 10000);
|
|
215
|
+
|
|
216
|
+
// Handle HTMX settlement
|
|
217
|
+
document.addEventListener('htmx:afterSettle', function(evt) {
|
|
218
|
+
if (evt.detail.target.id === 'content-area' &&
|
|
219
|
+
document.querySelector('.metrics-view')) {
|
|
220
|
+
updateRealtimeMetrics();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<style>
|
|
226
|
+
.metrics-section {
|
|
227
|
+
margin-bottom: var(--spacing-2xl);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.metrics-section h3 {
|
|
231
|
+
color: var(--accent-lime);
|
|
232
|
+
text-transform: uppercase;
|
|
233
|
+
letter-spacing: 0.1em;
|
|
234
|
+
margin-bottom: var(--spacing-lg);
|
|
235
|
+
padding-bottom: var(--spacing-md);
|
|
236
|
+
border-bottom: 2px solid var(--border-subtle);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.metrics-grid {
|
|
240
|
+
display: grid;
|
|
241
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
242
|
+
gap: var(--spacing-lg);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.metric-card {
|
|
246
|
+
background: var(--bg-card);
|
|
247
|
+
border: 1px solid var(--border-subtle);
|
|
248
|
+
border-radius: 2px;
|
|
249
|
+
padding: var(--spacing-xl);
|
|
250
|
+
text-align: center;
|
|
251
|
+
transition: all var(--transition-base);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.metric-card:hover {
|
|
255
|
+
border-color: var(--accent-lime);
|
|
256
|
+
box-shadow: var(--shadow-md);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.metric-label {
|
|
260
|
+
font-size: 0.85rem;
|
|
261
|
+
color: var(--text-secondary);
|
|
262
|
+
text-transform: uppercase;
|
|
263
|
+
letter-spacing: 0.05em;
|
|
264
|
+
margin-bottom: var(--spacing-md);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.metric-value {
|
|
268
|
+
font-size: 2.5rem;
|
|
269
|
+
color: var(--accent-lime);
|
|
270
|
+
font-weight: 700;
|
|
271
|
+
font-family: 'JetBrains Mono', monospace;
|
|
272
|
+
margin-bottom: var(--spacing-md);
|
|
273
|
+
word-break: break-word;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.metric-trend {
|
|
277
|
+
font-size: 0.9rem;
|
|
278
|
+
color: var(--status-success);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.metric-trend.down {
|
|
282
|
+
color: var(--status-blocked);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.chart-container {
|
|
286
|
+
background: var(--bg-card);
|
|
287
|
+
border: 1px solid var(--border-subtle);
|
|
288
|
+
border-radius: 2px;
|
|
289
|
+
padding: var(--spacing-xl);
|
|
290
|
+
height: 300px;
|
|
291
|
+
display: flex;
|
|
292
|
+
align-items: flex-end;
|
|
293
|
+
justify-content: space-around;
|
|
294
|
+
gap: var(--spacing-md);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.chart-bar {
|
|
298
|
+
flex: 1;
|
|
299
|
+
background: linear-gradient(180deg, var(--accent-lime) 0%, var(--accent-secondary) 100%);
|
|
300
|
+
border-radius: 2px 2px 0 0;
|
|
301
|
+
position: relative;
|
|
302
|
+
min-height: 20px;
|
|
303
|
+
transition: all var(--transition-base);
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.chart-bar:hover {
|
|
308
|
+
filter: brightness(1.2);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.chart-label {
|
|
312
|
+
position: absolute;
|
|
313
|
+
bottom: -25px;
|
|
314
|
+
left: 50%;
|
|
315
|
+
transform: translateX(-50%);
|
|
316
|
+
font-size: 0.75rem;
|
|
317
|
+
color: var(--text-secondary);
|
|
318
|
+
white-space: nowrap;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.chart-value {
|
|
322
|
+
position: absolute;
|
|
323
|
+
top: -25px;
|
|
324
|
+
left: 50%;
|
|
325
|
+
transform: translateX(-50%);
|
|
326
|
+
font-size: 0.8rem;
|
|
327
|
+
color: var(--accent-lime);
|
|
328
|
+
font-weight: 700;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
@media (max-width: 1024px) {
|
|
332
|
+
.metrics-grid {
|
|
333
|
+
grid-template-columns: repeat(2, 1fr);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@media (max-width: 768px) {
|
|
338
|
+
.metrics-grid {
|
|
339
|
+
grid-template-columns: 1fr;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.chart-container {
|
|
343
|
+
height: 250px;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
</style>
|