423 lines
15 KiB
HTML
423 lines
15 KiB
HTML
|
|
{% extends "shared/frontend/base.html" %}
|
||
|
|
|
||
|
|
{% block title %}Leverandør Detaljer - BMC Hub{% endblock %}
|
||
|
|
|
||
|
|
{% block extra_css %}
|
||
|
|
<style>
|
||
|
|
.vendor-header {
|
||
|
|
background: linear-gradient(135deg, var(--accent) 0%, #1e6ba8 100%);
|
||
|
|
padding: 2rem;
|
||
|
|
border-radius: var(--border-radius);
|
||
|
|
color: white;
|
||
|
|
margin-bottom: 2rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.vendor-avatar-large {
|
||
|
|
width: 80px;
|
||
|
|
height: 80px;
|
||
|
|
border-radius: 16px;
|
||
|
|
background: rgba(255,255,255,0.2);
|
||
|
|
color: white;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-weight: bold;
|
||
|
|
font-size: 2rem;
|
||
|
|
border: 3px solid rgba(255,255,255,0.3);
|
||
|
|
}
|
||
|
|
|
||
|
|
.vertical-nav {
|
||
|
|
position: sticky;
|
||
|
|
top: 100px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.vertical-nav .nav-link {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
padding: 0.75rem 1rem;
|
||
|
|
border-radius: 8px;
|
||
|
|
margin-bottom: 0.5rem;
|
||
|
|
transition: all 0.2s;
|
||
|
|
border-left: 3px solid transparent;
|
||
|
|
}
|
||
|
|
|
||
|
|
.vertical-nav .nav-link:hover,
|
||
|
|
.vertical-nav .nav-link.active {
|
||
|
|
background: var(--accent-light);
|
||
|
|
color: var(--accent);
|
||
|
|
border-left-color: var(--accent);
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-row {
|
||
|
|
padding: 1rem 0;
|
||
|
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-row:last-child {
|
||
|
|
border-bottom: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-label {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-size: 0.9rem;
|
||
|
|
font-weight: 500;
|
||
|
|
margin-bottom: 0.25rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-value {
|
||
|
|
color: var(--text-primary);
|
||
|
|
font-size: 1rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.category-badge-large {
|
||
|
|
padding: 0.5rem 1rem;
|
||
|
|
border-radius: 20px;
|
||
|
|
font-size: 1rem;
|
||
|
|
font-weight: 500;
|
||
|
|
display: inline-block;
|
||
|
|
}
|
||
|
|
|
||
|
|
.priority-indicator {
|
||
|
|
width: 50px;
|
||
|
|
height: 50px;
|
||
|
|
border-radius: 50%;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-weight: bold;
|
||
|
|
font-size: 1.25rem;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block content %}
|
||
|
|
<!-- Vendor Header -->
|
||
|
|
<div class="vendor-header" id="vendorHeader">
|
||
|
|
<div class="container-fluid">
|
||
|
|
<div class="row align-items-center">
|
||
|
|
<div class="col-auto">
|
||
|
|
<div class="vendor-avatar-large" id="vendorAvatar"></div>
|
||
|
|
</div>
|
||
|
|
<div class="col">
|
||
|
|
<div class="d-flex align-items-center gap-3 mb-2">
|
||
|
|
<h2 class="mb-0 fw-bold" id="vendorName">Loading...</h2>
|
||
|
|
<span class="badge bg-white text-dark" id="vendorStatus"></span>
|
||
|
|
</div>
|
||
|
|
<div class="d-flex gap-4 text-white-50">
|
||
|
|
<span id="vendorDomain"></span>
|
||
|
|
<span id="vendorCategory"></span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="col-auto">
|
||
|
|
<button class="btn btn-light" onclick="editVendor()">
|
||
|
|
<i class="bi bi-pencil me-2"></i>Rediger
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="container-fluid">
|
||
|
|
<div class="row">
|
||
|
|
<!-- Vertical Navigation -->
|
||
|
|
<div class="col-lg-2">
|
||
|
|
<div class="vertical-nav">
|
||
|
|
<nav class="nav flex-column">
|
||
|
|
<a class="nav-link active" href="#oversigt" data-tab="oversigt">
|
||
|
|
<i class="bi bi-info-circle me-2"></i>Oversigt
|
||
|
|
</a>
|
||
|
|
<a class="nav-link" href="#produkter" data-tab="produkter">
|
||
|
|
<i class="bi bi-box-seam me-2"></i>Produkter
|
||
|
|
</a>
|
||
|
|
<a class="nav-link" href="#fakturaer" data-tab="fakturaer">
|
||
|
|
<i class="bi bi-receipt me-2"></i>Fakturaer
|
||
|
|
</a>
|
||
|
|
<a class="nav-link" href="#aktivitet" data-tab="aktivitet">
|
||
|
|
<i class="bi bi-clock-history me-2"></i>Aktivitet
|
||
|
|
</a>
|
||
|
|
</nav>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Content Area -->
|
||
|
|
<div class="col-lg-10">
|
||
|
|
<div class="tab-content">
|
||
|
|
<!-- Oversigt Tab -->
|
||
|
|
<div class="tab-pane fade show active" id="oversigt">
|
||
|
|
<div class="row g-4">
|
||
|
|
<!-- Vendor Information -->
|
||
|
|
<div class="col-lg-6">
|
||
|
|
<div class="card p-4">
|
||
|
|
<h5 class="mb-4 fw-bold">Leverandør Information</h5>
|
||
|
|
<div id="vendorInfo">
|
||
|
|
<div class="text-center py-5">
|
||
|
|
<div class="spinner-border text-primary" role="status">
|
||
|
|
<span class="visually-hidden">Loading...</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Contact & System Info -->
|
||
|
|
<div class="col-lg-6">
|
||
|
|
<div class="card p-4 mb-4">
|
||
|
|
<h5 class="mb-4 fw-bold">Kontakt Information</h5>
|
||
|
|
<div id="contactInfo">
|
||
|
|
<div class="text-center py-5">
|
||
|
|
<div class="spinner-border text-primary" role="status">
|
||
|
|
<span class="visually-hidden">Loading...</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card p-4">
|
||
|
|
<h5 class="mb-4 fw-bold">System Information</h5>
|
||
|
|
<div id="systemInfo">
|
||
|
|
<div class="text-center py-5">
|
||
|
|
<div class="spinner-border text-primary" role="status">
|
||
|
|
<span class="visually-hidden">Loading...</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Produkter Tab -->
|
||
|
|
<div class="tab-pane fade" id="produkter">
|
||
|
|
<div class="card p-4">
|
||
|
|
<h5 class="mb-4 fw-bold">Produkter fra denne leverandør</h5>
|
||
|
|
<p class="text-muted">Produkt tracking kommer snart...</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Fakturaer Tab -->
|
||
|
|
<div class="tab-pane fade" id="fakturaer">
|
||
|
|
<div class="card p-4">
|
||
|
|
<h5 class="mb-4 fw-bold">Leverandør Fakturaer</h5>
|
||
|
|
<p class="text-muted">Faktura oversigt kommer snart...</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Aktivitet Tab -->
|
||
|
|
<div class="tab-pane fade" id="aktivitet">
|
||
|
|
<div class="card p-4">
|
||
|
|
<h5 class="mb-4 fw-bold">Aktivitetslog</h5>
|
||
|
|
<p class="text-muted">Aktivitetshistorik kommer snart...</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block extra_js %}
|
||
|
|
<script>
|
||
|
|
const vendorId = {{ vendor_id }};
|
||
|
|
|
||
|
|
async function loadVendor() {
|
||
|
|
try {
|
||
|
|
const response = await fetch(`/api/v1/vendors/${vendorId}`);
|
||
|
|
if (!response.ok) {
|
||
|
|
throw new Error('Vendor not found');
|
||
|
|
}
|
||
|
|
const vendor = await response.json();
|
||
|
|
displayVendor(vendor);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error loading vendor:', error);
|
||
|
|
document.getElementById('vendorName').textContent = 'Fejl ved indlæsning';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function displayVendor(vendor) {
|
||
|
|
// Header
|
||
|
|
document.getElementById('vendorName').textContent = vendor.name;
|
||
|
|
document.getElementById('vendorAvatar').textContent = getInitials(vendor.name);
|
||
|
|
document.getElementById('vendorStatus').textContent = vendor.is_active ? 'Aktiv' : 'Inaktiv';
|
||
|
|
document.getElementById('vendorStatus').className = `badge ${vendor.is_active ? 'bg-success' : 'bg-secondary'}`;
|
||
|
|
document.getElementById('vendorDomain').innerHTML = vendor.domain ? `<i class="bi bi-globe me-2"></i>${vendor.domain}` : '';
|
||
|
|
document.getElementById('vendorCategory').innerHTML = `${getCategoryIcon(vendor.category)} ${vendor.category}`;
|
||
|
|
|
||
|
|
// Update page title
|
||
|
|
document.title = `${vendor.name} - BMC Hub`;
|
||
|
|
|
||
|
|
// Vendor Info
|
||
|
|
document.getElementById('vendorInfo').innerHTML = `
|
||
|
|
${vendor.cvr_number ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">CVR-nummer</div>
|
||
|
|
<div class="info-value fw-semibold">${escapeHtml(vendor.cvr_number)}</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Kategori</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<span class="category-badge-large bg-light">
|
||
|
|
${getCategoryIcon(vendor.category)} ${escapeHtml(vendor.category)}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Prioritet</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<div class="priority-indicator ${getPriorityClass(vendor.priority)}">
|
||
|
|
${vendor.priority}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
${vendor.economic_supplier_number ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">e-conomic Leverandør Nr.</div>
|
||
|
|
<div class="info-value">${vendor.economic_supplier_number}</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
${vendor.notes ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Noter</div>
|
||
|
|
<div class="info-value">${escapeHtml(vendor.notes)}</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
`;
|
||
|
|
|
||
|
|
// Contact Info
|
||
|
|
document.getElementById('contactInfo').innerHTML = `
|
||
|
|
${vendor.email ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Email</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<a href="mailto:${escapeHtml(vendor.email)}" class="text-decoration-none">
|
||
|
|
<i class="bi bi-envelope me-2"></i>${escapeHtml(vendor.email)}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
${vendor.phone ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Telefon</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<a href="tel:${escapeHtml(vendor.phone)}" class="text-decoration-none">
|
||
|
|
<i class="bi bi-telephone me-2"></i>${escapeHtml(vendor.phone)}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
${vendor.website ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Website</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<a href="${escapeHtml(vendor.website)}" target="_blank" class="text-decoration-none">
|
||
|
|
<i class="bi bi-globe me-2"></i>${escapeHtml(vendor.website)}
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
${vendor.address ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Adresse</div>
|
||
|
|
<div class="info-value">
|
||
|
|
<i class="bi bi-geo-alt me-2"></i>
|
||
|
|
${escapeHtml(vendor.address)}
|
||
|
|
${vendor.postal_code || vendor.city ? `<br>${vendor.postal_code ? escapeHtml(vendor.postal_code) + ' ' : ''}${vendor.city ? escapeHtml(vendor.city) : ''}` : ''}
|
||
|
|
${vendor.country && vendor.country !== 'Danmark' ? `<br>${escapeHtml(vendor.country)}` : ''}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
${vendor.email_pattern ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Email Pattern</div>
|
||
|
|
<div class="info-value"><code>${escapeHtml(vendor.email_pattern)}</code></div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
`;
|
||
|
|
|
||
|
|
// System Info
|
||
|
|
document.getElementById('systemInfo').innerHTML = `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Oprettet</div>
|
||
|
|
<div class="info-value">${formatDate(vendor.created_at)}</div>
|
||
|
|
</div>
|
||
|
|
${vendor.updated_at ? `
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">Sidst opdateret</div>
|
||
|
|
<div class="info-value">${formatDate(vendor.updated_at)}</div>
|
||
|
|
</div>
|
||
|
|
` : ''}
|
||
|
|
<div class="info-row">
|
||
|
|
<div class="info-label">ID</div>
|
||
|
|
<div class="info-value"><code>#${vendor.id}</code></div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function getCategoryIcon(category) {
|
||
|
|
const icons = {
|
||
|
|
hardware: '🖥️',
|
||
|
|
software: '💻',
|
||
|
|
telecom: '📡',
|
||
|
|
services: '🛠️',
|
||
|
|
hosting: '☁️',
|
||
|
|
general: '📦'
|
||
|
|
};
|
||
|
|
return icons[category] || '📦';
|
||
|
|
}
|
||
|
|
|
||
|
|
function getPriorityClass(priority) {
|
||
|
|
if (priority >= 80) return 'bg-danger text-white';
|
||
|
|
if (priority >= 60) return 'bg-warning';
|
||
|
|
if (priority >= 40) return 'bg-info';
|
||
|
|
return 'bg-secondary text-white';
|
||
|
|
}
|
||
|
|
|
||
|
|
function getInitials(name) {
|
||
|
|
return name.split(' ').map(word => word[0]).join('').substring(0, 2).toUpperCase();
|
||
|
|
}
|
||
|
|
|
||
|
|
function escapeHtml(text) {
|
||
|
|
const div = document.createElement('div');
|
||
|
|
div.textContent = text;
|
||
|
|
return div.innerHTML;
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatDate(dateString) {
|
||
|
|
const date = new Date(dateString);
|
||
|
|
return date.toLocaleDateString('da-DK', {
|
||
|
|
year: 'numeric',
|
||
|
|
month: 'long',
|
||
|
|
day: 'numeric',
|
||
|
|
hour: '2-digit',
|
||
|
|
minute: '2-digit'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function editVendor() {
|
||
|
|
// TODO: Implement edit modal
|
||
|
|
alert('Edit funktion kommer snart!');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tab navigation
|
||
|
|
document.querySelectorAll('.vertical-nav .nav-link').forEach(link => {
|
||
|
|
link.addEventListener('click', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
const tab = link.dataset.tab;
|
||
|
|
|
||
|
|
// Update nav
|
||
|
|
document.querySelectorAll('.vertical-nav .nav-link').forEach(l => l.classList.remove('active'));
|
||
|
|
link.classList.add('active');
|
||
|
|
|
||
|
|
// Update content
|
||
|
|
document.querySelectorAll('.tab-pane').forEach(pane => {
|
||
|
|
pane.classList.remove('show', 'active');
|
||
|
|
});
|
||
|
|
document.getElementById(tab).classList.add('show', 'active');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Load vendor on page ready
|
||
|
|
document.addEventListener('DOMContentLoaded', loadVendor);
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|