agnes-the-ai-analyst/webapp/templates/activity_center.html
Petr c56905d34f Initial commit: OSS data distribution platform
Open-source AI data analyst platform extracted from internal repo.
Includes data sync engine, Keboola adapter, Flask web portal,
server deployment scripts, and configuration templates.
2026-03-08 23:31:28 +01:00

2570 lines
104 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Activity Center - Data Analyst Portal</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='style-custom.css') }}">
<style>
/* Activity Center Page Styles */
.container-activity {
max-width: 1200px;
margin: 0 auto;
padding: var(--space-6);
}
/* Top Header Bar (matches Data Catalog) */
.top-header {
background: var(--surface);
border-bottom: 1px solid var(--border);
padding: 0 32px;
height: 72px;
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 100;
}
.top-header-left {
display: flex;
align-items: center;
gap: 16px;
}
.header-back {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 6px;
color: var(--text-secondary);
text-decoration: none;
transition: all 0.15s ease;
}
.header-back:hover {
background: var(--border-light);
color: var(--text-primary);
}
.header-logo-group {
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
}
.header-logo svg {
display: block;
}
.header-subtitle {
font-size: 11px;
font-weight: 500;
color: var(--text-secondary);
letter-spacing: 0.4px;
text-transform: uppercase;
margin-top: 2px;
display: flex;
align-items: center;
gap: 8px;
}
.top-header-right {
display: flex;
align-items: center;
gap: var(--space-3);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.demo-badge {
display: inline-flex;
align-items: center;
padding: 3px 10px;
font-size: var(--text-xs);
font-weight: var(--font-bold);
border-radius: var(--radius-full);
background: rgba(245, 159, 10, 0.15);
color: #b45309;
border: 1px solid rgba(245, 159, 10, 0.4);
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Executive Pulse Stats Bar */
.exec-stats-bar {
display: flex;
align-items: center;
gap: var(--space-6);
padding: var(--space-4) var(--space-6);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-4);
}
.exec-stat {
text-align: center;
flex: 1;
}
.exec-stat .value {
font-size: var(--text-lg);
font-weight: var(--font-bold);
color: var(--text-primary);
}
.exec-stat .value .suffix {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-secondary);
}
.exec-stat .value.success {
color: var(--success);
}
.exec-stat .label {
font-size: var(--text-xs);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.3px;
margin-top: 2px;
}
.exec-divider {
width: 1px;
height: 36px;
background: var(--border);
flex-shrink: 0;
}
.exec-summary-sentence {
font-size: var(--text-sm);
color: var(--text-secondary);
text-align: center;
margin-bottom: var(--space-2);
}
/* ========== Tab Navigation ========== */
.tab-nav {
display: flex;
gap: var(--space-1);
border-bottom: 2px solid var(--border);
margin-bottom: var(--space-6);
padding: 0;
}
.tab-btn {
padding: var(--space-3) var(--space-5);
font-size: var(--text-base);
font-weight: var(--font-medium);
color: var(--text-secondary);
background: none;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: var(--space-2);
white-space: nowrap;
}
.tab-btn:hover {
color: var(--text-primary);
background: var(--background);
border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.tab-btn.active {
color: var(--primary);
border-bottom-color: var(--primary);
font-weight: var(--font-semibold);
}
.tab-btn .tab-count {
font-size: var(--text-xs);
font-weight: var(--font-medium);
background: var(--border);
color: var(--text-secondary);
padding: 1px 7px;
border-radius: var(--radius-full);
}
.tab-btn.active .tab-count {
background: var(--primary-light);
color: var(--primary);
}
.tab-panel {
display: none;
}
.tab-panel.active {
display: block;
}
/* ========== Section titles ========== */
.section {
margin-bottom: var(--space-8);
}
.section-header {
margin-bottom: var(--space-5);
}
.section-header h2 {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-1);
}
.section-header p {
font-size: var(--text-sm);
color: var(--text-secondary);
}
/* ========== Business Process Map ========== */
.process-category {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-4);
overflow: hidden;
}
.process-category-header {
padding: var(--space-4) var(--space-5);
background: var(--background);
border-bottom: 1px solid var(--border);
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: var(--text-primary);
display: flex;
align-items: center;
gap: var(--space-2);
}
.process-category-count {
font-size: var(--text-xs);
font-weight: var(--font-medium);
color: var(--text-secondary);
background: var(--border);
padding: 2px 8px;
border-radius: var(--radius-full);
}
.process-row {
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--border-light);
display: flex;
align-items: center;
gap: var(--space-3);
cursor: pointer;
transition: background 0.15s;
}
.process-row:last-child {
border-bottom: none;
}
.process-row:hover {
background: var(--background);
}
.process-status-dot {
width: 10px;
height: 10px;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.process-status-dot.optimized { background: var(--success); }
.process-status-dot.mature { background: var(--primary); }
.process-status-dot.developing { background: var(--warning); }
.process-status-dot.early {
background: var(--border);
border: 2px solid var(--text-secondary);
box-sizing: border-box;
}
.process-name {
font-size: var(--text-base);
font-weight: var(--font-medium);
color: var(--text-primary);
flex: 1;
}
.maturity-badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
font-size: var(--text-xs);
font-weight: var(--font-medium);
border-radius: var(--radius-full);
text-transform: uppercase;
letter-spacing: 0.3px;
}
.maturity-badge.optimized { background: rgba(16, 183, 127, 0.1); color: var(--success); }
.maturity-badge.mature { background: var(--primary-light); color: var(--primary); }
.maturity-badge.developing { background: rgba(245, 159, 10, 0.1); color: #b45309; }
.maturity-badge.early { background: var(--background); color: var(--text-secondary); }
.process-queries {
font-size: var(--text-sm);
color: var(--text-secondary);
white-space: nowrap;
}
.process-teams {
font-size: var(--text-xs);
color: var(--text-secondary);
white-space: nowrap;
}
.process-expand-icon {
color: var(--text-secondary);
transition: transform 0.2s;
flex-shrink: 0;
}
.process-expand-icon.expanded {
transform: rotate(180deg);
}
.process-detail {
display: none;
padding: var(--space-4) var(--space-5) var(--space-4) calc(var(--space-5) + 22px);
background: var(--background);
border-bottom: 1px solid var(--border-light);
}
.process-detail.visible {
display: block;
}
.process-detail-section {
margin-bottom: var(--space-3);
}
.process-detail-section:last-child {
margin-bottom: 0;
}
.process-detail-label {
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.3px;
margin-bottom: var(--space-1);
}
.process-detail-text {
font-size: var(--text-sm);
color: var(--text-primary);
line-height: 1.6;
}
.process-detail-tags {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
}
.process-detail-tag {
font-size: var(--text-xs);
background: var(--surface);
border: 1px solid var(--border);
padding: 2px 8px;
border-radius: var(--radius-sm);
color: var(--text-secondary);
}
/* ========== Team Maturity Leaderboard ========== */
.leaderboard-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
padding: var(--space-5);
margin-bottom: var(--space-6);
}
.leaderboard-card h3 {
font-size: var(--text-md);
font-weight: var(--font-semibold);
margin-bottom: var(--space-4);
}
.maturity-distribution {
display: flex;
height: 8px;
border-radius: var(--radius-full);
overflow: hidden;
margin-bottom: var(--space-4);
background: var(--background);
}
.maturity-segment { height: 100%; transition: width 0.3s ease; }
.maturity-segment.optimized { background: var(--success); }
.maturity-segment.mature { background: var(--primary); }
.maturity-segment.developing { background: var(--warning); }
.maturity-segment.early { background: var(--border); }
.maturity-legend {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
margin-bottom: var(--space-5);
font-size: var(--text-xs);
color: var(--text-secondary);
}
.maturity-legend-item {
display: flex;
align-items: center;
gap: 4px;
}
.maturity-legend-dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
}
.maturity-legend-dot.optimized { background: var(--success); }
.maturity-legend-dot.mature { background: var(--primary); }
.maturity-legend-dot.developing { background: var(--warning); }
.maturity-legend-dot.early { background: var(--border); }
/* Team rows in leaderboard - 2-column grid for full-width layout */
.team-rows-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 var(--space-8);
}
.team-row {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) 0;
border-bottom: 1px solid var(--border-light);
cursor: pointer;
transition: background 0.15s;
}
.team-row:hover {
background: var(--background);
margin: 0 calc(-1 * var(--space-3));
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
}
.team-row-name {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-primary);
min-width: 140px;
flex-shrink: 0;
}
.team-row-bar-wrap {
flex: 1;
height: 6px;
background: var(--background);
border-radius: var(--radius-full);
overflow: hidden;
}
.team-row-bar {
height: 100%;
border-radius: var(--radius-full);
transition: width 0.3s ease;
}
.team-row-bar.optimized { background: var(--success); }
.team-row-bar.mature { background: var(--primary); }
.team-row-bar.developing { background: var(--warning); }
.team-row-bar.early { background: var(--border); }
.team-row-score {
font-size: var(--text-sm);
font-weight: var(--font-semibold);
color: var(--text-primary);
min-width: 28px;
text-align: right;
}
.team-row-trend {
font-size: var(--text-sm);
min-width: 16px;
text-align: center;
}
.team-row-trend.up { color: var(--success); }
.team-row-trend.stable { color: var(--text-secondary); }
.team-row-trend.down { color: var(--error); }
/* ========== Live Activity Feed ========== */
.feed-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
}
.feed-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-5);
border-bottom: 1px solid var(--border);
}
.feed-header h3 {
font-size: var(--text-md);
font-weight: var(--font-semibold);
display: flex;
align-items: center;
gap: var(--space-2);
}
.feed-filters {
display: flex;
gap: var(--space-2);
}
.feed-filter-btn {
padding: var(--space-1) var(--space-3);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-md);
font-size: var(--text-xs);
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s;
}
.feed-filter-btn:hover { border-color: var(--text-secondary); }
.feed-filter-btn.active {
background: var(--primary-light);
border-color: var(--primary);
color: var(--primary);
}
.feed-list {
padding: var(--space-3);
}
/* Feed items in 2-column grid for full-width layout */
.feed-list-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 var(--space-4);
}
.feed-item {
display: flex;
gap: var(--space-3);
padding: var(--space-3);
border-radius: var(--radius-md);
transition: background 0.15s;
}
.feed-item:hover {
background: var(--background);
}
.feed-avatar {
width: 32px;
height: 32px;
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: white;
flex-shrink: 0;
}
.feed-body {
flex: 1;
min-width: 0;
}
.feed-person {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-primary);
}
.feed-team {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-left: var(--space-1);
}
.feed-process {
font-size: var(--text-sm);
font-weight: var(--font-medium);
margin-top: 2px;
}
.feed-process.optimized { color: var(--success); }
.feed-process.mature { color: var(--primary); }
.feed-process.developing { color: #b45309; }
.feed-process.early { color: var(--text-secondary); }
.feed-query {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-top: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.feed-meta {
display: flex;
align-items: center;
gap: var(--space-2);
margin-top: var(--space-1);
}
.feed-time {
font-size: var(--text-xs);
color: var(--text-secondary);
}
.feed-status {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.feed-status.success { background: var(--success); }
.feed-status.partial { background: var(--warning); }
/* ========== Data Opportunities ========== */
.opportunity-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
padding: var(--space-5);
margin-bottom: var(--space-4);
transition: all 0.15s;
}
.opportunity-card:hover {
border-color: var(--primary);
box-shadow: var(--shadow-md);
}
.opportunity-header {
display: flex;
align-items: flex-start;
gap: var(--space-3);
margin-bottom: var(--space-3);
}
.priority-badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
font-size: var(--text-xs);
font-weight: var(--font-bold);
border-radius: var(--radius-full);
text-transform: uppercase;
letter-spacing: 0.3px;
flex-shrink: 0;
}
.priority-badge.high { background: rgba(234, 88, 12, 0.1); color: var(--error); }
.priority-badge.medium { background: rgba(245, 159, 10, 0.1); color: #b45309; }
.priority-badge.low { background: var(--background); color: var(--text-secondary); }
.opportunity-title {
font-size: var(--text-md);
font-weight: var(--font-semibold);
color: var(--text-primary);
}
.opportunity-meta {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
margin-bottom: var(--space-3);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.opportunity-meta-label {
font-weight: var(--font-medium);
color: var(--text-secondary);
}
.opportunity-team-pills {
display: flex;
flex-wrap: wrap;
gap: var(--space-1);
}
.team-pill {
font-size: var(--text-xs);
background: var(--primary-light);
color: var(--primary);
padding: 1px 6px;
border-radius: var(--radius-full);
font-weight: var(--font-medium);
}
.opportunity-business-case {
font-size: var(--text-sm);
color: var(--text-secondary);
line-height: 1.6;
}
.opportunity-card {
cursor: pointer;
}
.opportunity-expand-icon {
color: var(--text-secondary);
transition: transform 0.2s;
flex-shrink: 0;
margin-left: auto;
}
.opportunity-expand-icon.expanded {
transform: rotate(180deg);
}
/* Opportunity Detail Panel */
.opportunity-detail {
display: none;
margin-top: var(--space-4);
padding-top: var(--space-4);
border-top: 1px solid var(--border);
}
.opportunity-detail.visible {
display: block;
}
.opp-detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-5);
margin-bottom: var(--space-4);
}
.opp-detail-section {
margin-bottom: var(--space-4);
}
.opp-detail-section:last-child {
margin-bottom: 0;
}
.opp-detail-label {
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: var(--space-2);
display: flex;
align-items: center;
gap: var(--space-2);
}
.opp-detail-label svg {
color: var(--primary);
}
.opp-detail-text {
font-size: var(--text-sm);
color: var(--text-primary);
line-height: 1.6;
}
/* Mermaid diagram container */
.opp-mermaid-wrap {
background: var(--background);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--space-4);
margin-bottom: var(--space-4);
overflow-x: auto;
}
.opp-mermaid-wrap .mermaid {
display: flex;
justify-content: center;
}
.opp-mermaid-wrap svg {
max-width: 100%;
height: auto;
}
.opp-diagram-legend {
display: flex;
gap: var(--space-4);
justify-content: center;
margin-top: var(--space-2);
font-size: var(--text-xs);
color: var(--text-secondary);
}
.opp-legend-item {
display: flex;
align-items: center;
gap: var(--space-1);
}
.opp-legend-dot {
width: 10px;
height: 10px;
border-radius: 2px;
}
.opp-legend-dot.existing {
background: #dbeafe;
border: 1px solid #2563eb;
}
.opp-legend-dot.new {
background: #fed7aa;
border: 1px solid #ea580c;
}
/* Join keys table */
.opp-join-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-sm);
margin-bottom: var(--space-2);
}
.opp-join-table th {
text-align: left;
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.3px;
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--border);
background: var(--background);
}
.opp-join-table td {
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--border-light);
color: var(--text-primary);
}
.opp-join-table .col-name {
font-family: var(--font-mono);
font-size: var(--text-xs);
background: var(--background);
padding: 2px 6px;
border-radius: var(--radius-sm);
color: var(--primary-dark);
}
.opp-join-table .tbl-new {
color: #ea580c;
font-weight: var(--font-medium);
}
.opp-join-table .tbl-existing {
color: #2563eb;
font-weight: var(--font-medium);
}
.opp-join-arrow {
color: var(--text-secondary);
text-align: center;
}
/* Team impact list */
.opp-beneficiary {
display: flex;
gap: var(--space-3);
padding: var(--space-3) 0;
border-bottom: 1px solid var(--border-light);
}
.opp-beneficiary:last-child {
border-bottom: none;
}
.opp-beneficiary-team {
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--primary);
background: var(--primary-light);
padding: 2px 8px;
border-radius: var(--radius-full);
white-space: nowrap;
height: fit-content;
flex-shrink: 0;
}
.opp-beneficiary-impact {
font-size: var(--text-sm);
color: var(--text-primary);
line-height: 1.5;
}
/* Enabled queries */
.opp-query-example {
display: flex;
align-items: flex-start;
gap: var(--space-2);
padding: var(--space-2) 0;
font-size: var(--text-sm);
color: var(--text-secondary);
}
.opp-query-example::before {
content: ">";
font-family: var(--font-mono);
color: var(--success);
font-weight: var(--font-bold);
flex-shrink: 0;
}
@media (max-width: 1024px) {
.opp-detail-grid {
grid-template-columns: 1fr;
}
}
/* ========== Team Detail Accordion ========== */
.team-accordion {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
margin-bottom: var(--space-3);
overflow: hidden;
}
.team-detail-header {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-4) var(--space-5);
cursor: pointer;
transition: background 0.15s;
}
.team-detail-header:hover {
background: var(--background);
}
.team-detail-name {
font-size: var(--text-base);
font-weight: var(--font-semibold);
color: var(--text-primary);
min-width: 140px;
}
.team-detail-stat {
font-size: var(--text-sm);
color: var(--text-secondary);
white-space: nowrap;
}
.team-detail-stat strong {
color: var(--text-primary);
}
.team-detail-highlight {
font-size: var(--text-xs);
color: var(--text-secondary);
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.team-detail-chevron {
color: var(--text-secondary);
transition: transform 0.2s;
flex-shrink: 0;
}
.team-detail-chevron.expanded {
transform: rotate(180deg);
}
.team-detail-body {
display: none;
border-top: 1px solid var(--border);
padding: var(--space-4) var(--space-5);
}
.team-detail-body.visible {
display: block;
}
.member-row {
display: flex;
align-items: flex-start;
gap: var(--space-3);
padding: var(--space-3) 0;
border-bottom: 1px solid var(--border-light);
}
.member-row:last-child {
border-bottom: none;
}
.member-status-dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
margin-top: 5px;
flex-shrink: 0;
}
.member-status-dot.active { background: var(--success); }
.member-status-dot.idle { background: var(--warning); }
.member-status-dot.offline { background: var(--border); }
.member-info {
flex: 1;
min-width: 0;
}
.member-name {
font-size: var(--text-sm);
font-weight: var(--font-medium);
color: var(--text-primary);
}
.member-role {
font-size: var(--text-xs);
color: var(--text-secondary);
}
.member-stats {
display: flex;
gap: var(--space-4);
font-size: var(--text-xs);
color: var(--text-secondary);
white-space: nowrap;
}
.member-recent-queries {
margin-top: var(--space-2);
}
.member-query-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-1) 0;
font-size: var(--text-xs);
}
.member-query-process {
padding: 1px 6px;
border-radius: var(--radius-sm);
font-weight: var(--font-medium);
flex-shrink: 0;
}
.member-query-process.optimized { background: rgba(16, 183, 127, 0.1); color: var(--success); }
.member-query-process.mature { background: var(--primary-light); color: var(--primary); }
.member-query-process.developing { background: rgba(245, 159, 10, 0.1); color: #b45309; }
.member-query-process.early { background: var(--background); color: var(--text-secondary); }
.member-query-text {
color: var(--text-secondary);
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.member-query-time {
color: var(--text-secondary);
white-space: nowrap;
flex-shrink: 0;
}
.member-query-status {
width: 6px;
height: 6px;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.member-query-status.success { background: var(--success); }
.member-query-status.partial { background: var(--warning); }
/* ========== Footer ========== */
.page-footer {
margin-top: var(--space-8);
padding-top: var(--space-5);
border-top: 1px solid var(--border);
text-align: center;
color: var(--text-secondary);
font-size: var(--text-sm);
}
/* ========== Overview Tab ========== */
.overview-hero {
display: grid;
grid-template-columns: auto 1fr auto;
gap: var(--space-6);
align-items: center;
padding: var(--space-6);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
margin-bottom: var(--space-6);
}
.overview-score-ring {
position: relative;
width: 120px;
height: 120px;
flex-shrink: 0;
}
.overview-score-ring svg {
transform: rotate(-90deg);
width: 120px;
height: 120px;
}
.overview-score-ring .ring-bg {
fill: none;
stroke: var(--border);
stroke-width: 8;
}
.overview-score-ring .ring-fill {
fill: none;
stroke-width: 8;
stroke-linecap: round;
transition: stroke-dashoffset 0.8s ease;
}
.overview-score-ring .ring-fill.level-optimized { stroke: var(--success); }
.overview-score-ring .ring-fill.level-mature { stroke: var(--primary); }
.overview-score-ring .ring-fill.level-developing { stroke: var(--warning); }
.overview-score-value {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.overview-score-value .number {
font-size: 32px;
font-weight: 700;
color: var(--text-primary);
line-height: 1;
}
.overview-score-value .label {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-top: 2px;
}
.overview-level-counts {
display: flex;
gap: var(--space-5);
}
.overview-level-count {
text-align: center;
}
.overview-level-count .count {
font-size: 28px;
font-weight: 700;
line-height: 1;
}
.overview-level-count .count.optimized { color: var(--success); }
.overview-level-count .count.mature { color: var(--primary); }
.overview-level-count .count.developing { color: var(--warning); }
.overview-level-count .level-label {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-top: 4px;
}
.overview-total-value {
text-align: right;
padding-left: var(--space-4);
border-left: 1px solid var(--border);
}
.overview-total-value .value {
font-size: 22px;
font-weight: 700;
color: var(--success);
line-height: 1;
}
.overview-total-value .label {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-top: 4px;
}
/* Roadmap Lanes */
.roadmap-section {
margin-bottom: var(--space-6);
}
.roadmap-section-title {
font-size: var(--text-lg);
font-weight: 600;
color: var(--text-primary);
margin-bottom: var(--space-4);
}
.roadmap-lanes {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: var(--space-4);
}
.roadmap-lane {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--space-4);
min-height: 120px;
}
.roadmap-lane-header {
display: flex;
align-items: center;
gap: var(--space-2);
margin-bottom: var(--space-3);
padding-bottom: var(--space-3);
border-bottom: 2px solid var(--border);
font-size: var(--text-sm);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.roadmap-lane-header.developing { border-bottom-color: var(--warning); color: var(--warning); }
.roadmap-lane-header.mature { border-bottom-color: var(--primary); color: var(--primary); }
.roadmap-lane-header.optimized { border-bottom-color: var(--success); color: var(--success); }
.roadmap-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3);
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-md);
margin-bottom: var(--space-2);
cursor: pointer;
transition: all 0.15s ease;
}
.roadmap-card:hover {
border-color: var(--primary);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.roadmap-card:last-child {
margin-bottom: 0;
}
.roadmap-card-name {
font-size: var(--text-sm);
font-weight: 500;
color: var(--text-primary);
}
.roadmap-card-score {
font-size: var(--text-xs);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.roadmap-card-arrow {
color: var(--text-tertiary);
margin-left: var(--space-2);
font-size: var(--text-xs);
}
/* Category Detail Cards */
.overview-details-title {
font-size: var(--text-lg);
font-weight: 600;
color: var(--text-primary);
margin-bottom: var(--space-4);
}
.overview-category-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
margin-bottom: var(--space-3);
overflow: hidden;
}
.overview-category-header {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-4);
cursor: pointer;
transition: background 0.15s ease;
}
.overview-category-header:hover {
background: var(--bg);
}
.overview-cat-level {
display: inline-flex;
align-items: center;
padding: 3px 10px;
font-size: var(--text-xs);
font-weight: 600;
border-radius: var(--radius-full);
text-transform: capitalize;
}
.overview-cat-level.optimized { background: rgba(16, 185, 129, 0.12); color: var(--success); }
.overview-cat-level.mature { background: rgba(59, 130, 246, 0.12); color: var(--primary); }
.overview-cat-level.developing { background: rgba(245, 159, 10, 0.12); color: var(--warning); }
.overview-cat-name {
font-size: var(--text-base);
font-weight: 600;
color: var(--text-primary);
flex: 1;
}
.overview-cat-meta {
display: flex;
align-items: center;
gap: var(--space-4);
font-size: var(--text-sm);
color: var(--text-secondary);
}
.overview-cat-chevron {
transition: transform 0.2s ease;
color: var(--text-tertiary);
}
.overview-cat-chevron.expanded {
transform: rotate(180deg);
}
.overview-category-body {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.overview-category-body.visible {
max-height: 800px;
}
.overview-category-content {
padding: 0 var(--space-4) var(--space-4);
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-4);
}
.overview-processes-list {
list-style: none;
padding: 0;
margin: 0 0 var(--space-3) 0;
}
.overview-processes-list li {
display: flex;
align-items: center;
gap: var(--space-2);
padding: 4px 0;
font-size: var(--text-sm);
color: var(--text-primary);
}
.overview-process-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.overview-process-dot.optimized { background: var(--success); }
.overview-process-dot.mature { background: var(--primary); }
.overview-process-dot.developing { background: var(--warning); }
.overview-process-dot.early { background: var(--text-tertiary); }
.overview-process-status {
font-size: var(--text-xs);
color: var(--text-secondary);
margin-left: auto;
text-transform: capitalize;
}
.overview-actions-section h4,
.overview-left-col h4 {
font-size: var(--text-sm);
font-weight: 600;
color: var(--text-primary);
margin: 0 0 var(--space-2) 0;
}
.overview-actions-list {
list-style: none;
padding: 0;
margin: 0 0 var(--space-3) 0;
}
.overview-actions-list li {
display: flex;
align-items: flex-start;
gap: var(--space-2);
padding: 4px 0;
font-size: var(--text-sm);
color: var(--text-secondary);
}
.overview-actions-list li::before {
content: "\2610";
color: var(--text-tertiary);
flex-shrink: 0;
}
.overview-effort-timeline {
display: flex;
gap: var(--space-3);
font-size: var(--text-xs);
color: var(--text-secondary);
margin-bottom: var(--space-3);
}
.overview-effort-timeline span {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
background: var(--bg);
border-radius: var(--radius-sm);
}
/* Timeline Comparison Widget */
.timeline-compare {
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--space-3) var(--space-4);
margin-bottom: var(--space-3);
}
.timeline-compare-header {
font-size: var(--text-xs);
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: var(--space-3);
}
.timeline-compare-row {
display: grid;
grid-template-columns: 100px 1fr 80px;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-2);
}
.timeline-compare-row:last-of-type {
margin-bottom: 0;
}
.timeline-compare-label {
font-size: var(--text-xs);
color: var(--text-secondary);
font-weight: 500;
}
.timeline-bar-track {
height: 22px;
background: var(--surface);
border-radius: var(--radius-sm);
overflow: hidden;
position: relative;
}
.timeline-bar-fill {
height: 100%;
border-radius: var(--radius-sm);
transition: width 0.6s ease;
}
.timeline-bar-fill.traditional {
background: linear-gradient(90deg, rgba(245, 159, 10, 0.3), rgba(245, 159, 10, 0.15));
border: 1px solid rgba(245, 159, 10, 0.4);
}
.timeline-bar-fill.ai-accelerated {
background: linear-gradient(90deg, rgba(99, 91, 255, 0.35), rgba(99, 91, 255, 0.15));
border: 1px solid rgba(99, 91, 255, 0.5);
}
.timeline-compare-value {
font-size: var(--text-sm);
font-weight: 600;
text-align: right;
}
.timeline-compare-value.traditional {
color: var(--warning);
}
.timeline-compare-value.ai-accelerated {
color: #635BFF;
}
.timeline-ai-note {
display: flex;
align-items: flex-start;
gap: var(--space-2);
margin-top: var(--space-3);
padding-top: var(--space-2);
border-top: 1px solid var(--border);
}
.timeline-ai-note svg {
flex-shrink: 0;
margin-top: 1px;
}
.timeline-ai-note-text {
font-size: var(--text-xs);
color: var(--text-secondary);
line-height: 1.5;
}
.timeline-ai-note-text strong {
color: #635BFF;
font-weight: 600;
}
.timeline-speedup-badge {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 1px 6px;
background: rgba(99, 91, 255, 0.1);
color: #635BFF;
font-size: 10px;
font-weight: 700;
border-radius: var(--radius-full);
margin-left: var(--space-2);
}
.overview-blockers {
font-size: var(--text-xs);
color: var(--text-secondary);
padding: var(--space-2) var(--space-3);
background: rgba(245, 159, 10, 0.06);
border-radius: var(--radius-sm);
border-left: 3px solid var(--warning);
margin-bottom: var(--space-3);
}
.overview-business-value {
padding: var(--space-3);
background: rgba(16, 185, 129, 0.06);
border-radius: var(--radius-md);
border-left: 3px solid var(--success);
}
.overview-business-value h4 {
font-size: var(--text-sm);
font-weight: 600;
color: var(--success);
margin: 0 0 4px 0;
}
.overview-business-value p {
font-size: var(--text-sm);
color: var(--text-primary);
margin: 0;
line-height: 1.5;
}
/* ========== Responsive ========== */
@media (max-width: 1024px) {
.team-rows-grid {
grid-template-columns: 1fr;
}
.feed-list-grid {
grid-template-columns: 1fr;
}
.roadmap-lanes {
grid-template-columns: 1fr;
}
.overview-hero {
grid-template-columns: auto 1fr;
}
.overview-total-value {
grid-column: 1 / -1;
text-align: left;
border-left: none;
padding-left: 0;
padding-top: var(--space-3);
border-top: 1px solid var(--border);
}
}
@media (max-width: 768px) {
.container-activity {
padding: var(--space-4);
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: var(--space-3);
}
.exec-stats-bar {
flex-wrap: wrap;
gap: var(--space-4);
padding: var(--space-4);
}
.exec-stat {
min-width: calc(50% - var(--space-4));
}
.exec-divider {
display: none;
}
.tab-nav {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.process-row {
flex-wrap: wrap;
}
.process-teams {
width: 100%;
margin-top: var(--space-1);
}
.team-detail-header {
flex-wrap: wrap;
}
.member-stats {
flex-wrap: wrap;
}
.feed-header {
flex-direction: column;
align-items: flex-start;
gap: var(--space-3);
}
.opportunity-header {
flex-direction: column;
}
.overview-hero {
grid-template-columns: 1fr;
text-align: center;
}
.overview-score-ring {
margin: 0 auto;
}
.overview-level-counts {
justify-content: center;
}
.overview-total-value {
text-align: center;
}
.overview-category-content {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<!-- Top Header Bar (matches Data Catalog pattern) -->
<header class="top-header">
<div class="top-header-left">
<a href="{{ url_for('dashboard') }}" class="header-back" title="Back to Dashboard">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
</a>
<div class="header-logo-group">
<div class="header-logo">
<svg width="120" height="30" viewBox="0 0 395 100" xmlns="http://www.w3.org/2000/svg">
<path d="M390.16321,40.175397 C393.472414,43.4361562 395,48.2443368 395,54.1631395 L395,76.4805472 C395,79.3112789 392.794993,81.45803 389.993855,81.45803 C387.019974,81.45803 384.984322,79.39593 384.984322,77.0798767 L384.984322,75.3631531 C381.929151,79.0539397 377.25833,81.9727085 370.382501,81.9727085 C361.979087,81.9727085 354.507127,77.0798767 354.507127,67.9815799 L354.507127,67.8122778 C354.507127,58.0266143 362.063765,53.2218197 373.014284,53.2218197 C378.023816,53.2218197 381.587053,53.9938374 385.069,55.1078455 L385.069,53.9938374 C385.069,47.5569702 381.163665,44.1235228 373.949126,44.1235228 C370.04379,44.1235228 366.819264,44.8108895 364.014739,45.9248976 C363.421995,46.0975858 362.913929,46.1822368 362.402475,46.1822368 C360.028113,46.1822368 358.073752,44.296211 358.073752,41.8921207 C358.073752,40.0027088 359.347304,38.3740223 360.87489,37.7746927 C365.118936,36.1426201 369.447659,35.1132631 375.307356,35.1132631 C382.013829,35.1132631 387.019974,36.9146379 390.16321,40.175397 Z M385.238356,64.7208208 L385.238356,61.6327498 C382.606573,60.6033928 379.124626,59.827989 375.053323,59.827989 C368.431527,59.827989 364.526192,62.6621068 364.526192,67.3822504 L364.526192,67.5515525 C368.346849,71.9297058 373.268317,74.4184472 380.059468,74.4184472 Z M343.983384,17.9494125 C346.869199,17.9494125 349.162271,20.2654658 349.162271,23.0961975 L349.162271,76.307859 C349.162271,79.2266278 346.869199,81.45803 343.983384,81.45803 C341.182246,81.45803 338.889174,79.2266278 338.889174,76.307859 L338.889174,23.0961975 C338.889174,20.2654658 341.097568,17.9494125 343.983384,17.9494125 Z M309.096174,34.7678868 C322.847832,34.7678868 332.948187,45.325568 332.948187,58.2839535 L332.948187,58.4566417 C332.948187,71.3303762 322.763154,82.0573596 308.926819,82.0573596 C295.259839,82.0573596 285.156097,71.4996783 285.156097,58.6293299 L285.156097,58.4566417 C285.156097,45.4982562 295.344517,34.7678868 309.096174,34.7678868 Z M322.678476,58.6293299 L322.678476,58.4566417 C322.678476,50.472353 316.991522,43.8661836 308.926819,43.8661836 C300.69276,43.8661836 295.429195,50.3910879 295.429195,58.2839535 L295.429195,58.4566417 C295.429195,66.3528934 301.116148,72.9624488 309.096174,72.9624488 C317.414911,72.9624488 322.678476,66.4375444 322.678476,58.6293299 Z M258.418269,34.7678868 C272.169926,34.7678868 282.270281,45.325568 282.270281,58.2839535 L282.270281,58.4566417 C282.270281,71.3303762 272.085248,82.0573596 258.248913,82.0573596 C244.581934,82.0573596 234.478191,71.4996783 234.478191,58.6293299 L234.478191,58.4566417 C234.478191,45.4982562 244.666611,34.7678868 258.418269,34.7678868 Z M272.000571,58.6293299 L272.000571,58.4566417 C272.000571,50.472353 266.313617,43.8661836 258.248913,43.8661836 C250.014854,43.8661836 244.751289,50.3910879 244.751289,58.2839535 L244.751289,58.4566417 C244.751289,66.3528934 250.438243,72.9624488 258.418269,72.9624488 C266.737005,72.9624488 272.000571,66.4375444 272.000571,58.6293299 Z M210.626179,34.7678868 C221.15331,34.7678868 231.426407,43.1788169 231.426407,58.2839535 L231.426407,58.4566417 C231.426407,73.4737412 221.237987,81.9727085 210.626179,81.9727085 C203.157606,81.9727085 198.490172,78.1938848 195.346936,73.9918058 L195.346936,76.307859 C195.346936,79.1385907 193.057251,81.45803 190.168048,81.45803 C187.36691,81.45803 185.077225,79.1385907 185.077225,76.307859 L185.077225,23.0961975 C185.077225,20.1774286 187.282232,17.9494125 190.168048,17.9494125 C193.057251,17.9494125 195.346936,20.1774286 195.346936,23.0961975 L195.346936,43.266854 C198.659527,38.5467105 203.326962,34.7678868 210.626179,34.7678868 Z M220.983954,58.4566417 L220.983954,58.2839535 C220.983954,49.5310331 215.127645,43.7781465 208.167139,43.7781465 C201.206632,43.7781465 195.092903,49.6156841 195.092903,58.2839535 L195.092903,58.4566417 C195.092903,67.1249111 201.206632,72.9624488 208.167139,72.9624488 C215.212323,72.9624488 220.983954,67.3822504 220.983954,58.4566417 Z M158.339397,34.7678868 C172.599121,34.7678868 179.644305,46.6122642 179.644305,57.0819084 C179.644305,60.0006772 177.439297,62.0593912 174.807515,62.0593912 L146.708069,62.0593912 C147.812266,69.4409643 152.991154,73.5617783 159.61295,73.5617783 C163.941673,73.5617783 167.335555,72.0177429 170.221371,69.6136525 C170.986857,69.014323 171.664279,68.6689466 172.853154,68.6689466 C175.146226,68.6689466 176.927844,70.4703213 176.927844,72.8744117 C176.927844,74.1611079 176.3351,75.278502 175.569614,76.0505198 C171.494923,79.7413063 166.404101,82.0573596 159.440207,82.0573596 C146.454036,82.0573596 136.438359,72.5324214 136.438359,58.5412928 L136.438359,58.3719907 C136.438359,45.4102191 145.519194,34.7678868 158.339397,34.7678868 Z M146.623392,55.1958826 L169.628627,55.1958826 C168.947818,48.49829 165.04587,43.266854 158.254719,43.266854 C151.971635,43.266854 147.558233,48.1562997 146.623392,55.1958826 Z M114.621998,47.384282 L134.65674,72.5324214 C135.503517,73.6464294 136.099648,74.6757864 136.099648,76.307859 C136.099648,79.2266278 133.806576,81.45803 130.836082,81.45803 C128.797044,81.45803 127.523491,80.428673 126.422681,78.9692886 L107.322781,54.3358277 L97.645814,63.7761149 L97.645814,76.2198219 C97.645814,79.1385907 95.3527421,81.45803 92.4669263,81.45803 C89.4964329,81.45803 87.2033609,79.1385907 87.2033609,76.2198219 L87.2033609,25.7576271 C87.2033609,22.8388582 89.4964329,20.522805 92.4669263,20.522805 C95.3527421,20.522805 97.645814,22.8388582 97.645814,25.7576271 L97.645814,51.1631057 L125.82655,22.4968679 C127.015425,21.2067856 128.288977,20.522805 130.155274,20.522805 C133.04109,20.522805 134.995451,22.8388582 134.995451,25.4156367 C134.995451,27.0443233 134.314642,28.2463685 133.129154,29.3637626 L114.621998,47.384282 Z M29.0196247,62.5097349 C15.2069994,62.5097349 4.64599757,45.6777165 4.64599757,24.3591914 C4.64599757,3.04405242 13.4457034,0 29.0196247,0 C44.5935459,0 52.6311525,3.04405242 52.6311525,24.3591914 C52.6311525,45.6777165 41.5790201,62.5097349 29.0196247,62.5097349 Z M56.7160044,81.9659364 C57.8506855,83.8519622 57.207135,86.2865269 55.2832579,87.397149 C54.6363203,87.7696137 53.9284148,87.9456879 53.2306706,87.9456879 C51.8453435,87.9456879 50.4938876,87.2481631 49.7385625,85.9919412 C48.4345261,83.831646 47.1000056,82.2943826 45.5961298,80.8891748 C44.092254,79.4907392 42.3851517,78.2379034 40.4307905,76.7785189 C39.4756262,76.070836 38.5238489,75.4579623 37.5754587,74.9365117 C37.6872333,75.1396743 37.7990079,75.3462229 37.9141695,75.5595436 C40.6577268,80.7198727 43.4893488,87.8813531 43.5028972,96.03833 C43.5028972,98.2257136 41.6907946,100 39.4553035,100 C37.2164253,100 35.4043227,98.2257136 35.4043227,96.03833 C35.4178711,89.8350997 33.1959285,83.8688924 30.8520499,79.4467206 C30.0933378,78.0042664 29.3278515,76.7311143 28.6368815,75.6678969 C27.9831697,76.6803237 27.2583287,77.8823689 26.5334876,79.236786 C24.1523509,83.679274 21.8558919,89.7402905 21.8694403,96.03833 C21.8694403,98.2257136 20.0573377,100 17.8218466,100 C15.5863555,100 13.7742529,98.2257136 13.7742529,96.03833 C13.7878013,87.8813531 16.6160362,80.7198727 19.3629806,75.5595436 C19.4747551,75.3462229 19.5865297,75.1396743 19.7016913,74.9365117 C18.7499141,75.4579623 17.8015239,76.070836 16.8463595,76.7785189 C14.8886113,78.2379034 13.181509,79.4907392 11.6810203,80.8891748 C10.1771445,82.2943826 8.83923692,83.831646 7.53520045,85.9953273 C6.40051937,87.8813531 3.91776941,88.507771 1.99389223,87.397149 C0.0666279455,86.2865269 -0.573535412,83.8519622 0.561145671,81.9659364 C2.25469953,79.1453628 4.15825406,76.944435 6.1092281,75.1362882 C8.06358925,73.3247554 10.0314988,71.8958453 11.9418276,70.4669353 C12.8461853,69.7897267 13.7742529,69.1666949 14.722643,68.6012257 C13.7742529,68.0357566 12.8461853,67.4093387 11.9418276,66.7321302 C7.57245863,63.471371 3.77551089,59.0729015 0.561145671,53.7331121 C-0.573535412,51.8437003 0.0666279455,49.4125216 1.99389223,48.3018996 C3.91776941,47.1912776 6.40051937,47.8176955 7.53520045,49.7037213 C10.319403,54.3324417 13.4795745,57.9148749 16.8429724,60.4205465 C20.4502422,63.0921342 23.9931568,64.4160769 27.6376847,64.6260116 C27.9696213,64.6158535 28.3049449,64.6056953 28.6368815,64.6056953 C28.9722052,64.6056953 29.3041417,64.6158535 29.6360783,64.6260116 C33.2806062,64.4160769 36.8269079,63.0921342 40.4307905,60.4205465 C43.7941885,57.9148749 46.9577471,54.3324417 49.7385625,49.7037213 C50.8766307,47.8176955 53.3559936,47.1912776 55.2832579,48.3018996 C57.207135,49.4125216 57.8506855,51.8437003 56.7160044,53.7297261 C53.5016392,59.0729015 49.7013043,63.471371 45.3319354,66.7321302 C44.4275776,67.4093387 43.4995101,68.0357566 42.5545071,68.6012257 C43.4995101,69.1666949 44.4275776,69.7897267 45.3319354,70.4669353 C47.2456513,71.8958453 49.2101737,73.3247554 51.167922,75.1362882 C53.118896,76.944435 55.0190635,79.1453628 56.7160044,81.9659364 Z M23.3123482,48.6845224 C21.0768571,48.6845224 19.2647545,50.4621948 19.2647545,52.6495784 C19.2647545,54.8437341 21.0768571,56.6180205 23.3123482,56.6180205 C25.5478393,56.6180205 27.3599419,54.8437341 27.3599419,52.6495784 C27.3599419,50.4621948 25.5478393,48.6845224 23.3123482,48.6845224 Z M33.9614148,48.6845224 C31.7259237,48.6845224 29.9138211,50.4621948 29.9138211,52.6495784 C29.9138211,54.8437341 31.7259237,56.6180205 33.9614148,56.6180205 C36.1969059,56.6180205 38.0090085,54.8437341 38.0090085,52.6495784 C38.0090085,50.4621948 36.1969059,48.6845224 33.9614148,48.6845224 Z" fill="#0073D1" fill-rule="nonzero"/>
</svg>
</div>
<span class="header-subtitle">Activity Center <span class="demo-badge">DEMO</span></span>
</div>
</div>
<div class="top-header-right">
{% if session.user.picture %}
<img src="{{ session.user.picture }}" alt="Profile" class="avatar-v2">
{% endif %}
{{ session.user.email }}
</div>
</header>
<div class="container-activity">
<!-- Executive Pulse - always visible -->
<div class="exec-stats-bar">
<div class="exec-stat">
<div class="value">{{ activity.executive_summary.active_today }}</div>
<div class="label">Active Today</div>
</div>
<div class="exec-divider"></div>
<div class="exec-stat">
<div class="value">{{ activity.executive_summary.business_processes_identified }}</div>
<div class="label">Business Processes</div>
</div>
<div class="exec-divider"></div>
<div class="exec-stat">
<div class="value">{{ activity.executive_summary.decisions_supported_this_week }}<span class="suffix">/wk</span></div>
<div class="label">Decisions Supported</div>
</div>
<div class="exec-divider"></div>
<div class="exec-stat">
<div class="value">{{ activity.executive_summary.avg_success_rate }}<span class="suffix">%</span></div>
<div class="label">Success Rate</div>
</div>
<div class="exec-divider"></div>
<div class="exec-stat">
<div class="value success">{{ activity.executive_summary.adoption_trend }}</div>
<div class="label">Adoption Trend</div>
</div>
</div>
<div class="exec-summary-sentence">
{{ activity.executive_summary.active_this_week }} analysts across {{ activity.executive_summary.teams_active }} teams powering {{ activity.executive_summary.business_processes_identified }} business processes with data-driven decisions
</div>
<!-- Tab Navigation -->
<nav class="tab-nav" role="tablist">
<button class="tab-btn active" role="tab" aria-selected="true" data-tab="overview" onclick="switchTab('overview', this)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 000 20 14.5 14.5 0 000-20"/><path d="M2 12h20"/></svg>
Overview
</button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="processes" onclick="switchTab('processes', this)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path 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 2"/></svg>
Processes
<span class="tab-count">{{ activity.business_processes | length }}</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="teams" onclick="switchTab('teams', this)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg>
Teams
<span class="tab-count">{{ activity.teams | length }}</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="activity" onclick="switchTab('activity', this)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
Activity
<span class="tab-count">{{ activity.activity_feed | length }}</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" data-tab="opportunities" onclick="switchTab('opportunities', this)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Opportunities
<span class="tab-count">{{ activity.data_opportunities | length }}</span>
</button>
</nav>
<!-- Tab Panel: Overview -->
<div class="tab-panel active" id="tab-overview" role="tabpanel">
<div class="section">
<!-- Hero: Score + Counts + Value -->
<div class="overview-hero">
<div class="overview-score-ring">
{% set score = activity.maturity_roadmap.summary.overall_score %}
{% set circumference = 2 * 3.14159 * 48 %}
{% set dash_offset = circumference * (1 - score / 100) %}
{% if score >= 80 %}{% set ring_class = "level-optimized" %}
{% elif score >= 60 %}{% set ring_class = "level-mature" %}
{% else %}{% set ring_class = "level-developing" %}{% endif %}
<svg viewBox="0 0 120 120">
<circle class="ring-bg" cx="60" cy="60" r="48"/>
<circle class="ring-fill {{ ring_class }}" cx="60" cy="60" r="48"
stroke-dasharray="{{ circumference }}"
stroke-dashoffset="{{ dash_offset }}"/>
</svg>
<div class="overview-score-value">
<div class="number">{{ score }}</div>
<div class="label">Maturity</div>
</div>
</div>
<div class="overview-level-counts">
<div class="overview-level-count">
<div class="count optimized">{{ activity.maturity_roadmap.summary.optimized_count }}</div>
<div class="level-label">Optimized</div>
</div>
<div class="overview-level-count">
<div class="count mature">{{ activity.maturity_roadmap.summary.mature_count }}</div>
<div class="level-label">Mature</div>
</div>
<div class="overview-level-count">
<div class="count developing">{{ activity.maturity_roadmap.summary.developing_count }}</div>
<div class="level-label">Developing</div>
</div>
</div>
<div class="overview-total-value">
<div class="value">{{ activity.maturity_roadmap.summary.total_potential_value }}</div>
<div class="label">Estimated annual value</div>
</div>
</div>
<!-- Roadmap Lanes -->
<div class="roadmap-section">
<h2 class="roadmap-section-title">Maturity Roadmap</h2>
<div class="roadmap-lanes">
<div class="roadmap-lane">
<div class="roadmap-lane-header developing">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
Developing
</div>
{% for cat in activity.maturity_roadmap.categories %}
{% if cat.current_level == 'developing' %}
<div class="roadmap-card" onclick="scrollToOverviewCategory('{{ cat.id }}')">
<div>
<div class="roadmap-card-name">{{ cat.name }}</div>
<div class="roadmap-card-score">{{ cat.score }}/100</div>
</div>
<span class="roadmap-card-arrow">&#8594;</span>
</div>
{% endif %}
{% endfor %}
</div>
<div class="roadmap-lane">
<div class="roadmap-lane-header mature">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
Mature
</div>
{% for cat in activity.maturity_roadmap.categories %}
{% if cat.current_level == 'mature' %}
<div class="roadmap-card" onclick="scrollToOverviewCategory('{{ cat.id }}')">
<div>
<div class="roadmap-card-name">{{ cat.name }}</div>
<div class="roadmap-card-score">{{ cat.score }}/100</div>
</div>
<span class="roadmap-card-arrow">&#8594;</span>
</div>
{% endif %}
{% endfor %}
</div>
<div class="roadmap-lane">
<div class="roadmap-lane-header optimized">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
Optimized
</div>
{% for cat in activity.maturity_roadmap.categories %}
{% if cat.current_level == 'optimized' %}
<div class="roadmap-card" onclick="scrollToOverviewCategory('{{ cat.id }}')">
<div>
<div class="roadmap-card-name">{{ cat.name }}</div>
<div class="roadmap-card-score">{{ cat.score }}/100</div>
</div>
<span class="roadmap-card-arrow">&#9733;</span>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
<!-- Category Details -->
<div class="overview-details-section">
<h2 class="overview-details-title">Category Details</h2>
{% for cat in activity.maturity_roadmap.categories %}
<div class="overview-category-card" id="overview-cat-{{ cat.id }}">
<div class="overview-category-header" onclick="toggleOverviewCategory('{{ cat.id }}')">
<span class="overview-cat-level {{ cat.current_level }}">{{ cat.current_level }}</span>
<span class="overview-cat-name">{{ cat.name }}</span>
<span class="overview-cat-meta">
<span>{{ cat.score }}/100</span>
<span>{{ cat.process_count }} processes</span>
</span>
<svg class="overview-cat-chevron" id="overview-chevron-{{ cat.id }}" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
</div>
<div class="overview-category-body" id="overview-body-{{ cat.id }}">
<div class="overview-category-content">
<div class="overview-left-col">
<h4>Current Processes</h4>
<ul class="overview-processes-list">
{% for proc in cat.processes %}
<li>
<span class="overview-process-dot {{ proc.status }}"></span>
{{ proc.name }}
<span class="overview-process-status">{{ proc.status }}</span>
</li>
{% endfor %}
</ul>
{% if cat.advancement %}
<h4>Actions to reach {{ cat.advancement.target_level|capitalize }}</h4>
<ul class="overview-actions-list">
{% for action in cat.advancement.actions %}
<li>{{ action }}</li>
{% endfor %}
</ul>
<div class="overview-effort-timeline">
<span>Effort: {{ cat.advancement.effort|capitalize }}</span>
</div>
{% if cat.advancement.blockers %}
<div class="overview-blockers">
{{ cat.advancement.blockers }}
</div>
{% endif %}
<!-- Timeline Comparison -->
<div class="timeline-compare">
<div class="timeline-compare-header">Implementation Timeline</div>
<div class="timeline-compare-row">
<span class="timeline-compare-label">Traditional</span>
<div class="timeline-bar-track">
<div class="timeline-bar-fill traditional" style="width: 100%"></div>
</div>
<span class="timeline-compare-value traditional">{{ cat.advancement.timeline }}</span>
</div>
<div class="timeline-compare-row">
<span class="timeline-compare-label">With AI Data Analyst</span>
<div class="timeline-bar-track">
<div class="timeline-bar-fill ai-accelerated" style="width: 8%"></div>
</div>
<span class="timeline-compare-value ai-accelerated">
{{ cat.advancement.ai_timeline }}
<span class="timeline-speedup-badge">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10"/></svg>
Fast
</span>
</span>
</div>
<div class="timeline-ai-note">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#635BFF" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
<span class="timeline-ai-note-text">
<strong>AI Data Platform:</strong> {{ cat.advancement.ai_description }}
</span>
</div>
</div>
{% endif %}
</div>
<div class="overview-right-col">
<div class="overview-business-value">
<h4>Business Value</h4>
<p>{{ cat.business_value }}</p>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Tab Panel: Processes -->
<div class="tab-panel" id="tab-processes" role="tabpanel">
<div class="section">
<div class="section-header">
<h2>Business Process Intelligence</h2>
<p>Mapping data queries to active business processes across the organization</p>
</div>
{% set ns = namespace(cat_processes={}) %}
{% for process in activity.business_processes %}
{% if process.category not in ns.cat_processes %}
{% set _ = ns.cat_processes.update({process.category: []}) %}
{% endif %}
{% set _ = ns.cat_processes[process.category].append(process) %}
{% endfor %}
{% for category, processes in ns.cat_processes.items() %}
{% set cat_idx = loop.index %}
<div class="process-category">
<div class="process-category-header">
{{ category }}
<span class="process-category-count">{{ processes | length }}</span>
</div>
{% for process in processes %}
<div class="process-row" onclick="toggleProcessDetail('process-{{ cat_idx }}-{{ loop.index }}')">
<span class="process-status-dot {{ process.status }}"></span>
<span class="process-name">{{ process.name }}</span>
<span class="maturity-badge {{ process.status }}">{{ process.status }}</span>
<span class="process-queries">{{ process.queries_this_week }} queries/wk</span>
<span class="process-teams">{{ process.teams_involved | join(', ') }}</span>
<svg class="process-expand-icon" id="icon-process-{{ cat_idx }}-{{ loop.index }}" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6"/>
</svg>
</div>
<div class="process-detail" id="process-{{ cat_idx }}-{{ loop.index }}">
{% if process.description %}
<div class="process-detail-section">
<div class="process-detail-label">Description</div>
<div class="process-detail-text">{{ process.description }}</div>
</div>
{% endif %}
{% if process.sample_queries %}
<div class="process-detail-section">
<div class="process-detail-label">Sample Queries</div>
<div class="process-detail-tags">
{% for query in process.sample_queries %}
<span class="process-detail-tag">{{ query }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if process.data_sources %}
<div class="process-detail-section">
<div class="process-detail-label">Data Sources</div>
<div class="process-detail-tags">
{% for source in process.data_sources %}
<span class="process-detail-tag">{{ source }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if process.impact %}
<div class="process-detail-section">
<div class="process-detail-label">Impact</div>
<div class="process-detail-text">{{ process.impact }}</div>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
<!-- Tab Panel: Teams -->
<div class="tab-panel" id="tab-teams" role="tabpanel">
<!-- Team Maturity Leaderboard -->
<div class="leaderboard-card">
<h3>Team Maturity</h3>
{% set optimized_count = activity.teams | selectattr('maturity', 'equalto', 'optimized') | list | length %}
{% set mature_count = activity.teams | selectattr('maturity', 'equalto', 'mature') | list | length %}
{% set developing_count = activity.teams | selectattr('maturity', 'equalto', 'developing') | list | length %}
{% set early_count = activity.teams | selectattr('maturity', 'equalto', 'early') | list | length %}
{% set total_teams = activity.teams | length %}
<div class="maturity-distribution">
{% if optimized_count > 0 %}
<div class="maturity-segment optimized" style="width: {{ (optimized_count / total_teams * 100) | round(1) }}%"></div>
{% endif %}
{% if mature_count > 0 %}
<div class="maturity-segment mature" style="width: {{ (mature_count / total_teams * 100) | round(1) }}%"></div>
{% endif %}
{% if developing_count > 0 %}
<div class="maturity-segment developing" style="width: {{ (developing_count / total_teams * 100) | round(1) }}%"></div>
{% endif %}
{% if early_count > 0 %}
<div class="maturity-segment early" style="width: {{ (early_count / total_teams * 100) | round(1) }}%"></div>
{% endif %}
</div>
<div class="maturity-legend">
<span class="maturity-legend-item"><span class="maturity-legend-dot optimized"></span> Optimized ({{ optimized_count }})</span>
<span class="maturity-legend-item"><span class="maturity-legend-dot mature"></span> Mature ({{ mature_count }})</span>
<span class="maturity-legend-item"><span class="maturity-legend-dot developing"></span> Developing ({{ developing_count }})</span>
<span class="maturity-legend-item"><span class="maturity-legend-dot early"></span> Early ({{ early_count }})</span>
</div>
<div class="team-rows-grid">
{% for team in activity.teams | sort(attribute='maturity_score', reverse=True) %}
<div class="team-row" onclick="scrollToTeam('team-detail-{{ loop.index }}')">
<span class="team-row-name">{{ team.name }}</span>
<div class="team-row-bar-wrap">
<div class="team-row-bar {{ team.maturity }}" style="width: {{ team.maturity_score }}%"></div>
</div>
<span class="team-row-score">{{ team.maturity_score }}</span>
<span class="team-row-trend {% if team.trend == 'up' %}up{% elif team.trend == 'down' %}down{% else %}stable{% endif %}">
{% if team.trend == 'up' %}&#9650;{% elif team.trend == 'down' %}&#9660;{% else %}&#8212;{% endif %}
</span>
</div>
{% endfor %}
</div>
</div>
<!-- Team Detail Accordion -->
<div class="section">
<div class="section-header">
<h2>Team Details</h2>
</div>
{% for team in activity.teams | sort(attribute='maturity_score', reverse=True) %}
<div class="team-accordion" id="team-detail-{{ loop.index }}">
<div class="team-detail-header" onclick="toggleTeam('team-detail-{{ loop.index }}')">
<span class="team-detail-name">{{ team.name }}</span>
<span class="maturity-badge {{ team.maturity }}">{{ team.maturity }}</span>
<span class="team-detail-stat"><strong>{{ team.members | length }}</strong> members</span>
<span class="team-detail-stat"><strong>{{ team.active_count }}</strong> active</span>
<span class="team-detail-stat">{{ team.success_rate }}% success</span>
<span class="team-detail-highlight">{{ team.highlight }}</span>
<svg class="team-detail-chevron" id="chevron-team-detail-{{ loop.index }}" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6"/>
</svg>
</div>
<div class="team-detail-body" id="body-team-detail-{{ loop.index }}">
{% for member in team.members %}
<div class="member-row">
<span class="member-status-dot {{ member.status | lower }}"></span>
<div class="member-info">
<div class="member-name">{{ member.name }}</div>
<div class="member-role">{{ member.role }}</div>
</div>
<div class="member-stats">
<span>Last active: {{ member.last_active }}</span>
<span>{{ member.queries_today }} queries today</span>
</div>
{% if member.recent_queries %}
<div class="member-recent-queries">
{% for query in member.recent_queries %}
<div class="member-query-item">
<span class="member-query-process">{{ query.process }}</span>
<span class="member-query-text" title="{{ query.text }}">{{ query.text }}</span>
<span class="member-query-time">{{ query.timestamp }}</span>
<span class="member-query-status {{ query.status | lower }}"></span>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
<!-- Tab Panel: Activity -->
<div class="tab-panel" id="tab-activity" role="tabpanel">
<div class="feed-card">
<div class="feed-header">
<h3>
<span class="live-dot"></span>
Live Activity
</h3>
<div class="feed-filters">
<button class="feed-filter-btn active" onclick="filterActivity('all', this)">All</button>
<button class="feed-filter-btn" onclick="filterActivity('today', this)">Today</button>
<button class="feed-filter-btn" onclick="filterActivity('hour', this)">This Hour</button>
</div>
</div>
<div class="feed-list" id="activityFeed">
<div class="feed-list-grid">
{% for item in activity.activity_feed %}
<div class="feed-item" data-index="{{ loop.index }}">
<div class="feed-avatar" style="background: {{ ['#0073D1', '#10B77F', '#F59F0A', '#EA580C', '#7c3aed', '#0d9668', '#b45309', '#005BA3'][loop.index0 % 8] }}">
{{ item.person_name[:2] | upper }}
</div>
<div class="feed-body">
<div>
<span class="feed-person">{{ item.person_name }}</span>
<span class="feed-team">{{ item.team }}</span>
</div>
<div class="feed-process">{{ item.process_name }}</div>
<div class="feed-query" title="{{ item.query_text }}">{{ item.query_text }}</div>
<div class="feed-meta">
<span class="feed-time">{{ item.timestamp }}</span>
<span class="feed-status {{ item.status | lower }}"></span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Tab Panel: Opportunities -->
<div class="tab-panel" id="tab-opportunities" role="tabpanel">
<div class="section">
<div class="section-header">
<h2>Unmet Data Needs</h2>
<p>Strategic opportunities where additional data would unlock new capabilities. Click any card to see integration details.</p>
</div>
{% for opp in activity.data_opportunities %}
<div class="opportunity-card" onclick="toggleOpportunity('opp-{{ loop.index }}')">
<div class="opportunity-header">
<span class="priority-badge {{ opp.priority | lower }}">{{ opp.priority }}</span>
<span class="opportunity-title">{{ opp.title }}</span>
<svg class="opportunity-expand-icon" id="icon-opp-{{ loop.index }}" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6 9l6 6 6-6"/>
</svg>
</div>
<div class="opportunity-meta">
<div>
<span class="opportunity-meta-label">Requested by:</span>
<span class="opportunity-team-pills">
{% for team in opp.requested_by %}
<span class="team-pill">{{ team }}</span>
{% endfor %}
</span>
</div>
{% if opp.potential_processes %}
<div>
<span class="opportunity-meta-label">Related:</span>
{{ opp.potential_processes | join(', ') }}
</div>
{% endif %}
</div>
<div class="opportunity-business-case">{{ opp.business_case }}</div>
<!-- Expandable detail panel -->
<div class="opportunity-detail" id="opp-{{ loop.index }}" onclick="event.stopPropagation()">
{% if opp.mermaid %}
<div class="opp-detail-section">
<div class="opp-detail-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>
Data Integration Map
</div>
<div class="opp-mermaid-wrap">
<pre class="mermaid" data-opp-id="opp-{{ loop.index }}">{{ opp.mermaid }}</pre>
<div class="opp-diagram-legend">
<span class="opp-legend-item"><span class="opp-legend-dot existing"></span> Existing table</span>
<span class="opp-legend-item"><span class="opp-legend-dot new"></span> New data source</span>
</div>
</div>
</div>
{% endif %}
<div class="opp-detail-grid">
<!-- Left column: Integration + Join Keys -->
<div>
{% if opp.integration_description %}
<div class="opp-detail-section">
<div class="opp-detail-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
Integration Path
</div>
<div class="opp-detail-text">{{ opp.integration_description }}</div>
</div>
{% endif %}
{% if opp.join_keys %}
<div class="opp-detail-section">
<div class="opp-detail-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 11-7.778 7.778 5.5 5.5 0 017.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>
Join Keys
</div>
<table class="opp-join-table">
<thead>
<tr>
<th>New Table</th>
<th></th>
<th>Column</th>
<th></th>
<th>Existing Table</th>
</tr>
</thead>
<tbody>
{% for jk in opp.join_keys %}
<tr>
<td><span class="tbl-new">{{ jk.new_table }}</span></td>
<td class="opp-join-arrow">.</td>
<td><span class="col-name">{{ jk.column }}</span></td>
<td class="opp-join-arrow">&rarr;</td>
<td><span class="tbl-existing">{{ jk.existing_table }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
<!-- Right column: Team Impact + Enabled Queries -->
<div>
{% if opp.beneficiaries %}
<div class="opp-detail-section">
<div class="opp-detail-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg>
Team Impact
</div>
{% for ben in opp.beneficiaries %}
<div class="opp-beneficiary">
<span class="opp-beneficiary-team">{{ ben.team }}</span>
<span class="opp-beneficiary-impact">{{ ben.impact }}</span>
</div>
{% endfor %}
</div>
{% endif %}
{% if opp.enabled_queries %}
<div class="opp-detail-section">
<div class="opp-detail-label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
Enabled Queries
</div>
{% for q in opp.enabled_queries %}
<div class="opp-query-example">{{ q }}</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- Footer -->
<footer class="page-footer">
{{ config.INSTANCE_COPYRIGHT or 'AI Data Analyst' }} | Activity Center
</footer>
</div>
<script>
// Tab switching with URL hash support
function switchTab(tabId, btn) {
// Deactivate all tabs and panels
document.querySelectorAll('.tab-btn').forEach(function(b) {
b.classList.remove('active');
b.setAttribute('aria-selected', 'false');
});
document.querySelectorAll('.tab-panel').forEach(function(p) {
p.classList.remove('active');
});
// Activate selected tab and panel
btn.classList.add('active');
btn.setAttribute('aria-selected', 'true');
document.getElementById('tab-' + tabId).classList.add('active');
// Update URL hash without scrolling
history.replaceState(null, null, '#' + tabId);
}
// Restore tab from URL hash on page load
(function() {
var hash = window.location.hash.replace('#', '');
var validTabs = ['overview', 'processes', 'teams', 'activity', 'opportunities'];
if (hash && validTabs.indexOf(hash) !== -1) {
var btn = document.querySelector('.tab-btn[data-tab="' + hash + '"]');
if (btn) {
switchTab(hash, btn);
}
}
})();
// Filter activity feed by time period
function filterActivity(period, btn) {
document.querySelectorAll('.feed-filter-btn').forEach(function(b) {
b.classList.remove('active');
});
btn.classList.add('active');
var items = document.querySelectorAll('.feed-item');
items.forEach(function(item, index) {
if (period === 'hour') {
item.style.display = (index < 5) ? 'flex' : 'none';
} else {
item.style.display = 'flex';
}
});
}
// Toggle team accordion
function toggleTeam(teamId) {
var body = document.getElementById('body-' + teamId);
var chevron = document.getElementById('chevron-' + teamId);
if (body.classList.contains('visible')) {
body.classList.remove('visible');
chevron.classList.remove('expanded');
} else {
body.classList.add('visible');
chevron.classList.add('expanded');
}
}
// Scroll from leaderboard to team detail
function scrollToTeam(teamId) {
var element = document.getElementById(teamId);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Expand the team if not already expanded
var body = document.getElementById('body-' + teamId);
var chevron = document.getElementById('chevron-' + teamId);
if (body && !body.classList.contains('visible')) {
body.classList.add('visible');
if (chevron) {
chevron.classList.add('expanded');
}
}
// Brief highlight effect
element.style.transition = 'box-shadow 0.3s ease';
element.style.boxShadow = '0 0 0 2px var(--primary)';
setTimeout(function() {
element.style.boxShadow = '';
}, 1500);
}
}
// Toggle process detail expansion
function toggleProcessDetail(processId) {
var detail = document.getElementById(processId);
var icon = document.getElementById('icon-' + processId);
if (detail.classList.contains('visible')) {
detail.classList.remove('visible');
if (icon) icon.classList.remove('expanded');
} else {
detail.classList.add('visible');
if (icon) icon.classList.add('expanded');
}
}
// Toggle opportunity detail with lazy Mermaid rendering
var renderedOpps = {};
function toggleOpportunity(oppId) {
var detail = document.getElementById(oppId);
var icon = document.getElementById('icon-' + oppId);
if (detail.classList.contains('visible')) {
detail.classList.remove('visible');
if (icon) icon.classList.remove('expanded');
} else {
detail.classList.add('visible');
if (icon) icon.classList.add('expanded');
// Lazy-render Mermaid diagram on first expand
if (!renderedOpps[oppId] && typeof mermaid !== 'undefined') {
var mermaidEl = detail.querySelector('.mermaid');
if (mermaidEl && !mermaidEl.getAttribute('data-processed')) {
mermaid.run({ nodes: [mermaidEl] });
renderedOpps[oppId] = true;
}
}
}
}
// Toggle overview category detail
function toggleOverviewCategory(catId) {
var body = document.getElementById('overview-body-' + catId);
var chevron = document.getElementById('overview-chevron-' + catId);
if (body.classList.contains('visible')) {
body.classList.remove('visible');
chevron.classList.remove('expanded');
} else {
body.classList.add('visible');
chevron.classList.add('expanded');
}
}
// Scroll from roadmap lane card to category detail
function scrollToOverviewCategory(catId) {
var element = document.getElementById('overview-cat-' + catId);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Expand if not already expanded
var body = document.getElementById('overview-body-' + catId);
var chevron = document.getElementById('overview-chevron-' + catId);
if (body && !body.classList.contains('visible')) {
body.classList.add('visible');
if (chevron) chevron.classList.add('expanded');
}
// Brief highlight
element.style.transition = 'box-shadow 0.3s ease';
element.style.boxShadow = '0 0 0 2px var(--primary)';
setTimeout(function() {
element.style.boxShadow = '';
}, 1500);
}
}
</script>
<!-- Mermaid.js for data integration diagrams -->
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
fontFamily: 'Inter, system-ui, sans-serif',
fontSize: '13px',
primaryColor: '#dbeafe',
primaryBorderColor: '#2563eb',
primaryTextColor: '#1e40af',
lineColor: '#94a3b8',
secondaryColor: '#f1f5f9',
tertiaryColor: '#f5f7fa',
edgeLabelBackground: '#f1f5f9',
},
flowchart: {
curve: 'basis',
padding: 12,
htmlLabels: true,
useMaxWidth: true,
},
});
// Expose mermaid globally for lazy rendering
window.mermaid = mermaid;
</script>
</body>
</html>