341 lines
15 KiB
HTML
341 lines
15 KiB
HTML
{{template "base" .}}
|
|
|
|
{{define "naslov"}}Izveštaji — NTech{{end}}
|
|
|
|
{{define "dodatni-css"}}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js"></script>
|
|
<style>
|
|
.izv-naslov {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--tekst-sporedni);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.toggle-red {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.toggle-switch {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 40px;
|
|
height: 22px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.toggle-switch input { opacity: 0; width: 0; height: 0; }
|
|
|
|
.toggle-slider {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: var(--ivica);
|
|
border-radius: 22px;
|
|
cursor: pointer;
|
|
transition: background 0.2s;
|
|
}
|
|
|
|
.toggle-slider::before {
|
|
content: '';
|
|
position: absolute;
|
|
width: 16px;
|
|
height: 16px;
|
|
left: 3px;
|
|
top: 3px;
|
|
background: #fff;
|
|
border-radius: 50%;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.toggle-switch input:checked + .toggle-slider { background: var(--sb-akcent); }
|
|
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(18px); }
|
|
|
|
.rang-broj {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
background: var(--pozadina);
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--tekst-sporedni);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.rang-broj.zlatni { background: #fef3c7; color: #92400e; }
|
|
|
|
.badge-dana {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: 20px;
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.badge-dana.upozorenje { background: #fffbeb; color: #b45309; }
|
|
.badge-dana.kritican { background: #fef2f2; color: #dc2626; }
|
|
|
|
@keyframes fadeInUp {
|
|
from { opacity: 0; transform: translateY(8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.izv-sekcija {
|
|
animation: fadeInUp 0.25s ease forwards;
|
|
opacity: 0;
|
|
}
|
|
.izv-sekcija:nth-child(1) { animation-delay: 0.04s; }
|
|
.izv-sekcija:nth-child(2) { animation-delay: 0.12s; }
|
|
.izv-sekcija:nth-child(3) { animation-delay: 0.20s; }
|
|
|
|
.izv-grid-kartica {
|
|
animation: fadeInUp 0.25s ease forwards;
|
|
opacity: 0;
|
|
}
|
|
.izv-grid-kartica:nth-child(1) { animation-delay: 0.24s; }
|
|
.izv-grid-kartica:nth-child(2) { animation-delay: 0.32s; }
|
|
</style>
|
|
{{end}}
|
|
|
|
{{define "sadrzaj"}}
|
|
<div style="display:flex;flex-direction:column;gap:20px;">
|
|
|
|
<!-- 1. mesečni prihod -->
|
|
<div class="kartica izv-sekcija">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;margin-bottom:4px;">
|
|
<div class="izv-naslov" style="margin-bottom:0;">Mesečni prihod — poslednjih 12 meseci</div>
|
|
<div class="toggle-red" style="margin-bottom:0;">
|
|
<span style="font-size:13px;color:var(--tekst-sporedni);">Grafikon</span>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="toggle-grafikon" checked>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="grafikon-wrapper" style="margin:16px 0;position:relative;height:260px;">
|
|
<canvas id="grafikon-prihod"></canvas>
|
|
</div>
|
|
|
|
<div style="overflow-x:auto;">
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<thead>
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Mesec</th>
|
|
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Prodaja</th>
|
|
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Servis</th>
|
|
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Ukupno</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .MesecniPrihodi}}
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-glavni);">{{.MesecPrikaz}}</td>
|
|
<td style="padding:8px 12px;text-align:right;font-size:13px;color:var(--tekst-sporedni);">
|
|
{{if gt .Prodaja 0.0}}{{printf "%.0f" .Prodaja}} din{{else}}—{{end}}
|
|
</td>
|
|
<td style="padding:8px 12px;text-align:right;font-size:13px;color:var(--tekst-sporedni);">
|
|
{{if gt .Servis 0.0}}{{printf "%.0f" .Servis}} din{{else}}—{{end}}
|
|
</td>
|
|
<td style="padding:8px 12px;text-align:right;font-size:13px;font-weight:500;color:var(--tekst-glavni);">
|
|
{{if gt .Ukupno 0.0}}{{printf "%.0f" .Ukupno}} din{{else}}—{{end}}
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. stari otvoreni nalozi -->
|
|
<div class="kartica izv-sekcija">
|
|
<div class="izv-naslov">Stari otvoreni nalozi — bez završetka duže od 14 dana</div>
|
|
{{if .StariNalozi}}
|
|
<div style="overflow-x:auto;">
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<thead>
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Nalog</th>
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Uređaj</th>
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Klijent</th>
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Status</th>
|
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Primljeno</th>
|
|
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Dana</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .StariNalozi}}
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<td style="padding:8px 12px;">
|
|
<a href="/servis/{{.ID}}" style="font-size:13px;font-family:monospace;color:var(--sb-akcent);text-decoration:none;">{{.BrojNaloga}}</a>
|
|
</td>
|
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-glavni);">{{.Uredjaj}}</td>
|
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-sporedni);">{{.KlijentNaziv}}</td>
|
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-sporedni);">{{.Status}}</td>
|
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-sporedni);">{{.DatumPrijema}}</td>
|
|
<td style="padding:8px 12px;text-align:right;">
|
|
{{if gt .DanaProslo 30}}
|
|
<span class="badge-dana kritican">{{.DanaProslo}} dana</span>
|
|
{{else}}
|
|
<span class="badge-dana upozorenje">{{.DanaProslo}} dana</span>
|
|
{{end}}
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{else}}
|
|
<div style="font-size:13px;color:var(--tekst-sporedni);padding:8px 0;">Nema zaostalih naloga. Sve je u redu.</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 izv-sekcija">
|
|
|
|
<!-- 3. top 10 artikala -->
|
|
<div class="kartica izv-grid-kartica">
|
|
<div class="izv-naslov">Najprodavaniji artikli — top 10</div>
|
|
{{if .TopArtikli}}
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<thead>
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<th style="padding:6px 8px;text-align:left;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:32px;">#</th>
|
|
<th style="padding:6px 8px;text-align:left;font-size:11px;font-weight:500;color:var(--tekst-sporedni);">Artikal</th>
|
|
<th style="padding:6px 8px;text-align:right;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:60px;">Kom.</th>
|
|
<th style="padding:6px 8px;text-align:right;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:100px;">Prihod</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopArtikli}}
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<td style="padding:7px 8px;">
|
|
<span class="rang-broj {{if eq .Rang 1}}zlatni{{end}}">{{.Rang}}</span>
|
|
</td>
|
|
<td style="padding:7px 8px;">
|
|
<div style="font-size:13px;color:var(--tekst-glavni);">{{.Naziv}}</div>
|
|
<div style="font-size:11px;color:var(--tekst-sporedni);margin-top:1px;">{{.Kategorija}}</div>
|
|
</td>
|
|
<td style="padding:7px 8px;text-align:right;font-size:13px;color:var(--tekst-glavni);">{{.UkupnoKolicina}}</td>
|
|
<td style="padding:7px 8px;text-align:right;font-size:13px;font-weight:500;color:var(--tekst-glavni);">{{printf "%.0f" .UkupnoPrihod}} din</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<div style="font-size:13px;color:var(--tekst-sporedni);padding:8px 0;">Nema podataka o prodaji.</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<!-- 4. top 10 klijenata -->
|
|
<div class="kartica izv-grid-kartica">
|
|
<div class="izv-naslov">Najvažniji klijenti — top 10</div>
|
|
{{if .TopKlijenti}}
|
|
<table style="width:100%;border-collapse:collapse;">
|
|
<thead>
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<th style="padding:6px 8px;text-align:left;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:32px;">#</th>
|
|
<th style="padding:6px 8px;text-align:left;font-size:11px;font-weight:500;color:var(--tekst-sporedni);">Klijent</th>
|
|
<th style="padding:6px 8px;text-align:right;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:60px;">Naloga</th>
|
|
<th style="padding:6px 8px;text-align:right;font-size:11px;font-weight:500;color:var(--tekst-sporedni);width:110px;">Vrednost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .TopKlijenti}}
|
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
|
<td style="padding:7px 8px;">
|
|
<span class="rang-broj {{if eq .Rang 1}}zlatni{{end}}">{{.Rang}}</span>
|
|
</td>
|
|
<td style="padding:7px 8px;font-size:13px;color:var(--tekst-glavni);">{{.Naziv}}</td>
|
|
<td style="padding:7px 8px;text-align:right;font-size:13px;color:var(--tekst-sporedni);">{{.BrojNaloga}}</td>
|
|
<td style="padding:7px 8px;text-align:right;font-size:13px;font-weight:500;color:var(--tekst-glavni);">{{printf "%.0f" .UkupnoVrednost}} din</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<div style="font-size:13px;color:var(--tekst-sporedni);padding:8px 0;">Nema podataka o klijentima.</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var podaci = {{.GrafikonJSON}};
|
|
|
|
var isDark = document.documentElement.getAttribute('data-tema') !== 'svetla';
|
|
var tekstBoja = getComputedStyle(document.documentElement).getPropertyValue('--tekst-sporedni').trim() || '#6b7280';
|
|
var ivicaBoja = getComputedStyle(document.documentElement).getPropertyValue('--ivica').trim() || '#e5e7eb';
|
|
|
|
var ctx = document.getElementById('grafikon-prihod').getContext('2d');
|
|
var grafikon = new Chart(ctx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: podaci.labele,
|
|
datasets: [
|
|
{
|
|
label: 'Prodaja',
|
|
data: podaci.prodaja,
|
|
backgroundColor: 'rgba(79, 126, 248, 0.75)',
|
|
borderRadius: 4,
|
|
borderSkipped: false,
|
|
},
|
|
{
|
|
label: 'Servis',
|
|
data: podaci.servis,
|
|
backgroundColor: 'rgba(22, 163, 74, 0.75)',
|
|
borderRadius: 4,
|
|
borderSkipped: false,
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
labels: { color: tekstBoja, font: { size: 12 }, boxWidth: 12, padding: 16 }
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
label: function(ctx) {
|
|
return ' ' + ctx.dataset.label + ': ' + Math.round(ctx.parsed.y).toLocaleString('sr-RS') + ' din';
|
|
}
|
|
}
|
|
}
|
|
},
|
|
scales: {
|
|
x: {
|
|
stacked: false,
|
|
ticks: { color: tekstBoja, font: { size: 11 } },
|
|
grid: { color: ivicaBoja }
|
|
},
|
|
y: {
|
|
ticks: {
|
|
color: tekstBoja,
|
|
font: { size: 11 },
|
|
callback: function(v) { return Math.round(v).toLocaleString('sr-RS') + ' din'; }
|
|
},
|
|
grid: { color: ivicaBoja }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
document.getElementById('toggle-grafikon').addEventListener('change', function() {
|
|
document.getElementById('grafikon-wrapper').style.display = this.checked ? 'block' : 'none';
|
|
});
|
|
})();
|
|
</script>
|
|
{{end}}
|