auto-claude: subtask-5-3 - Add collaboration matrix section showing company p

Added collaboration matrix section to IT audit dashboard with:
- CSS styles for match type cards following combo-grid pattern
- Six match type categories: M365 licensing, backup replication,
  Teams federation, shared monitoring, collective purchasing,
  and knowledge sharing
- Company pairs display with status badges (suggested, contacted,
  active, declined)
- Empty state with friendly message when no matches exist
- Responsive grid layout matching social_media.html patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-09 09:04:58 +01:00
parent b9ba00fae9
commit aab2b298a7
2 changed files with 322 additions and 3 deletions

View File

@ -3,7 +3,7 @@
"spec": "001-audyt-it",
"state": "building",
"subtasks": {
"completed": 17,
"completed": 18,
"total": 28,
"in_progress": 1,
"failed": 0
@ -18,8 +18,8 @@
"max": 1
},
"session": {
"number": 18,
"number": 19,
"started_at": "2026-01-09T08:11:54.054044"
},
"last_update": "2026-01-09T09:00:29.741214"
"last_update": "2026-01-09T09:03:14.642451"
}

View File

@ -249,6 +249,129 @@
color: var(--primary);
}
/* Collaboration Matrix */
.collab-matrix-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-lg);
}
.match-type-card {
background: var(--background);
border-radius: var(--radius);
padding: var(--spacing-md);
}
.match-type-card h3 {
font-size: var(--font-size-base);
color: var(--text-primary);
margin-bottom: var(--spacing-sm);
display: flex;
justify-content: space-between;
align-items: center;
}
.match-type-card h3 .count {
background: var(--primary);
color: white;
padding: 2px 8px;
border-radius: var(--radius-sm);
font-size: var(--font-size-sm);
}
.match-type-icon {
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: var(--spacing-xs);
flex-shrink: 0;
}
.match-type-icon.m365 { background: #fff3e0; color: #d83b01; }
.match-type-icon.backup { background: #e8f5e9; color: #2e7d32; }
.match-type-icon.teams { background: #e8f4fc; color: #0078d4; }
.match-type-icon.monitoring { background: #fee2e2; color: #dc2626; }
.match-type-icon.purchasing { background: #f3e8ff; color: #7c3aed; }
.match-type-icon.knowledge { background: #fef3c7; color: #92400e; }
.match-pairs-list {
max-height: 200px;
overflow-y: auto;
font-size: var(--font-size-sm);
}
.match-pair {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-xs) 0;
border-bottom: 1px solid var(--border);
}
.match-pair:last-child {
border-bottom: none;
}
.match-pair-companies {
display: flex;
align-items: center;
gap: var(--spacing-xs);
flex: 1;
min-width: 0;
}
.match-pair-companies a {
color: var(--text-secondary);
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 120px;
}
.match-pair-companies a:hover {
color: var(--primary);
}
.match-pair-separator {
color: var(--text-muted);
font-size: var(--font-size-xs);
flex-shrink: 0;
}
.match-status-badge {
display: inline-block;
padding: 2px 6px;
border-radius: var(--radius-sm);
font-size: var(--font-size-xs);
font-weight: 500;
flex-shrink: 0;
margin-left: var(--spacing-xs);
}
.match-status-badge.suggested {
background: #e0f2fe;
color: #0284c7;
}
.match-status-badge.contacted {
background: #fef3c7;
color: #92400e;
}
.match-status-badge.active {
background: #dcfce7;
color: #166534;
}
.match-status-badge.declined {
background: #fee2e2;
color: #991b1b;
}
/* Empty state */
.empty-state {
text-align: center;
@ -856,6 +979,202 @@
{% endif %}
</div>
<!-- Collaboration Matrix -->
<div class="section">
<h2>Macierz wspolpracy</h2>
{% if collaboration_matches %}
<div class="collab-matrix-grid">
<!-- Shared M365 Licensing -->
{% set m365_matches = collaboration_matches|selectattr('match_type', 'equalto', 'shared_m365_licensing')|list %}
{% if m365_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon m365">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
<path d="M11.5 2.25L2.25 6v12l9.25 3.75L21.75 18V6L11.5 2.25z"/>
</svg>
</span>
Wspoldzielone licencje M365
</span>
<span class="count">{{ m365_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in m365_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Backup Replication -->
{% set backup_matches = collaboration_matches|selectattr('match_type', 'equalto', 'backup_replication')|list %}
{% if backup_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon backup">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"/>
</svg>
</span>
Replikacja backupow
</span>
<span class="count">{{ backup_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in backup_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Teams Federation -->
{% set teams_matches = collaboration_matches|selectattr('match_type', 'equalto', 'teams_federation')|list %}
{% if teams_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon teams">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</span>
Federacja Teams
</span>
<span class="count">{{ teams_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in teams_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Shared Monitoring -->
{% set monitoring_matches = collaboration_matches|selectattr('match_type', 'equalto', 'shared_monitoring')|list %}
{% if monitoring_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon monitoring">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
<path d="M3 3v18h18V3H3zm16 16H5V5h14v14zM7 7h2v2H7V7zm4 0h2v2h-2V7zm4 0h2v2h-2V7z"/>
</svg>
</span>
Wspolny monitoring
</span>
<span class="count">{{ monitoring_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in monitoring_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Collective Purchasing -->
{% set purchasing_matches = collaboration_matches|selectattr('match_type', 'equalto', 'collective_purchasing')|list %}
{% if purchasing_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon purchasing">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"/>
</svg>
</span>
Zakupy grupowe
</span>
<span class="count">{{ purchasing_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in purchasing_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Knowledge Sharing -->
{% set knowledge_matches = collaboration_matches|selectattr('match_type', 'equalto', 'knowledge_sharing')|list %}
{% if knowledge_matches %}
<div class="match-type-card">
<h3>
<span>
<span class="match-type-icon knowledge">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
</span>
Wymiana wiedzy
</span>
<span class="count">{{ knowledge_matches|length }}</span>
</h3>
<div class="match-pairs-list">
{% for match in knowledge_matches %}
<div class="match-pair">
<div class="match-pair-companies">
<a href="{{ url_for('company_detail', company_id=match.company_a_id) }}" title="{{ match.company_a_name }}">{{ match.company_a_name }}</a>
<span class="match-pair-separator"></span>
<a href="{{ url_for('company_detail', company_id=match.company_b_id) }}" title="{{ match.company_b_name }}">{{ match.company_b_name }}</a>
</div>
<span class="match-status-badge {{ match.status }}">{{ match.status }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% else %}
<div class="empty-state">
<svg width="64" height="64" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<h3>Brak dopasowan wspolpracy</h3>
<p>Dopasowania pojawia sie automatycznie, gdy firmy z podobna infrastruktura IT wypelnia audyty.</p>
</div>
{% endif %}
</div>
<!-- Company Audit Table -->
<div class="section">
<h2>Lista firm</h2>