Lepší GUI, responzivní na mobilu

master
Kowalski 2026-10-17 14:18:23 +02:00
parent e649c5ae9c
commit ad63238961
6 changed files with 298 additions and 62 deletions

View File

@ -17,7 +17,7 @@
</script>
{* naše vlastní styly *}
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-4" rel="stylesheet">
<link href="{$basePath}/css/layout.css?v={$appVersion ?? '1.0'}-6" rel="stylesheet">
</head>
<body>

View File

@ -37,7 +37,7 @@
<div class="card shadow-sm border-0 p-3 bg-white">
<div class="table-responsive">
<table class="table table-hover align-middle w-100" id="databaze-table">
<table class="table table-hover table-cards align-middle w-100" id="databaze-table">
<thead class="table-light">
<tr>
<th>Databáze</th>
@ -54,36 +54,36 @@
<tbody>
{foreach $loginy as $login}
<tr n:class="!$login->AKTIVNI ? 'table-light text-muted'">
<td class="fw-bold" data-order="{$login->DATABASE}">
<td data-label="Databáze" class="fw-bold" data-order="{$login->DATABASE}">
<i class="bi bi-database me-1 text-muted"></i>{$login->DATABASE}
</td>
<td>{$login->SERVER}</td>
<td>
<td data-label="Server">{$login->SERVER}</td>
<td data-label="Login">
<a href="mailto:{$login->EMAIL}" class="text-decoration-none">{$login->EMAIL}</a>
</td>
<td class="text-center">{$login->FIRMA_ID}</td>
<td data-order="{if $login->POSLEDNI_PRIHLASENI}{$login->POSLEDNI_PRIHLASENI|date:'YmdHis'}{/if}">
<td data-label="Firma ID" class="text-center">{$login->FIRMA_ID}</td>
<td data-label="Poslední přihlášení" data-order="{if $login->POSLEDNI_PRIHLASENI}{$login->POSLEDNI_PRIHLASENI|date:'YmdHis'}{/if}">
{if $login->POSLEDNI_PRIHLASENI}
{$login->POSLEDNI_PRIHLASENI|date:'d.m.Y H:i'}
{else}
<span class="text-muted">—</span>
{/if}
</td>
<td class="text-center" data-order="{$login->AKTIVNI}">
<td data-label="Stav" class="text-center" data-order="{$login->AKTIVNI}">
{if $login->AKTIVNI}
<span class="badge bg-success">Aktivní</span>
{else}
<span class="badge bg-secondary">Neaktivní</span>
{/if}
</td>
<td class="text-center" data-order="{$login->IS_ADMIN}">
<td data-label="Role" class="text-center" data-order="{$login->IS_ADMIN}">
{if $login->IS_ADMIN}
<span class="badge bg-dark">Admin</span>
{else}
<span class="badge bg-light text-dark border">Uživatel</span>
{/if}
</td>
<td data-order="{$login->SDF_SOUBOR}">
<td data-label="Poslední import SDF" data-order="{$login->SDF_SOUBOR}">
{if $login->SDF_SOUBOR}
<div class="fw-semibold">{$login->SDF_SOUBOR}</div>
<div class="small text-muted">
@ -99,7 +99,7 @@
<span class="text-muted">—</span>
{/if}
</td>
<td class="text-end text-nowrap">
<td class="text-end text-nowrap cell-actions">
<button type="button"
class="btn btn-sm btn-success js-sdf-import"
data-login-id="{$login->ID}"
@ -127,7 +127,8 @@ $(document).ready(function() {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/cs.json'
},
order: [[0, 'asc']],
pageLength: 25
pageLength: 25,
autoWidth: false
});
const $form = $('form[id$="-sdfImportForm"], #frm-sdfImportForm').first();

View File

@ -68,7 +68,7 @@
<!-- Tabulka Faktur -->
<div class="card shadow-sm border-0 p-3 bg-white">
<div class="table-responsive">
<table class="table table-hover align-middle w-100" id="faktury-table">
<table class="table table-hover table-cards align-middle w-100" id="faktury-table">
<thead class="table-light">
<tr>
<th>Číslo</th>
@ -85,39 +85,39 @@
{foreach $faktury as $f}
<tr>
<!-- Číslo dokladu -->
<td data-order="{$f->CISLO}">
<td data-label="Číslo" data-order="{$f->CISLO}">
<a n:href="FakturaEdit:edit $f->ID" class="fw-bold text-decoration-none">
{$f->CISLO}
</a>
</td>
<!-- Odběratel -->
<td>{$f->O_PRAJM ?: '—'}</td>
<td data-label="Odběratel">{$f->O_PRAJM ?: '—'}</td>
<!-- Datum vystavení -->
<td data-order="{$f->DATUM_VYSTAVENI|date:'Ymd'}">
<td data-label="Vystaveno" data-order="{$f->DATUM_VYSTAVENI|date:'Ymd'}">
{$f->DATUM_VYSTAVENI|date:'d.m.Y'}
</td>
<!-- Datum splatnosti -->
<td data-order="{$f->DATUM_SPLATNOSTI|date:'Ymd'}"
<td data-label="Splatnost" data-order="{$f->DATUM_SPLATNOSTI|date:'Ymd'}"
class="{if $f->STAV_UHRADY !== 'PAID' && ($f->DATUM_SPLATNOSTI|date:'Y-m-d') < $dnes}text-danger fw-bold{/if}">
{$f->DATUM_SPLATNOSTI|date:'d.m.Y'}
</td>
<!-- Částka celkem -->
<td class="text-end fw-bold" data-order="{$f->CASTKA_CELKEM}">
<td data-label="Částka" class="text-end fw-bold" data-order="{$f->CASTKA_CELKEM}">
{$f->CASTKA_CELKEM|number:2, ',', ' '} CZK
</td>
<!-- Zaplaceno -->
<td class="text-end {if $f->STAV_UHRADY === 'PAID'}text-success{elseif $f->ZAPLACENO > 0}text-warning{else}text-danger{/if}"
<td data-label="Zaplaceno" class="text-end {if $f->STAV_UHRADY === 'PAID'}text-success{elseif $f->ZAPLACENO > 0}text-warning{else}text-danger{/if}"
data-order="{$f->ZAPLACENO}">
{$f->ZAPLACENO|number:2, ',', ' '} CZK
</td>
<!-- Stav -->
<td class="text-center" data-order="{$f->STAV_UHRADY}">
<td data-label="Stav" class="text-center" data-order="{$f->STAV_UHRADY}">
<span class="badge {if $f->STAV_UHRADY === 'PAID'}bg-success{elseif $f->STAV_UHRADY === 'PARTIAL'}bg-info text-dark{else}bg-warning text-dark{/if}">
{if $f->STAV_UHRADY === 'PAID'}Zaplaceno{elseif $f->STAV_UHRADY === 'PARTIAL'}Částečně{else}Neuhrazeno{/if}
</span>
</td>
<!-- Sloupec AKCE -->
<td class="text-end text-nowrap">
<td class="text-end text-nowrap cell-actions">
<a href="{link FakturaEdit:edit $f->ID}#uhrady"
class="btn btn-sm {if $f->STAV_UHRADY === 'PAID'}btn-outline-secondary{else}btn-outline-success{/if} me-1"
title="Upravit úhrady">
@ -166,7 +166,8 @@ $(document).ready(function() {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/cs.json'
},
order: [[2, 'desc']], // Výchozí řazení podle data vystavení sestupně
pageLength: 25
pageLength: 25,
autoWidth: false
});
});
</script>

View File

@ -60,7 +60,7 @@
<h5 class="mb-3"><i class="bi bi-list-check me-2"></i>Položky faktury</h5>
<div class="table-responsive">
<table class="table table-bordered align-middle" id="items-table">
<table class="table table-bordered table-cards align-middle" id="items-table">
<thead class="table-light">
<tr>
<th style="width: 40px;" class="text-center"></th> <!-- Sloupec pro Drag Handle -->
@ -76,16 +76,16 @@
<tbody id="items-body">
{foreach $polozky as $index => $p}
<tr class="item-row">
<td class="text-center align-middle drag-handle" style="cursor: grab;">
<td data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
<i class="bi bi-grip-vertical text-muted fs-5"></i>
</td>
<td><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" required></td>
<td><input type="number" step="0.01" name="polozky[{$index}][POCET]" value="{$p->POCET}" class="form-control item-pocet text-end" required></td>
<td><input type="text" name="polozky[{$index}][JEDNOTKA]" value="{$p->JEDNOTKA}" class="form-control"></td>
<td><input type="number" step="0.01" name="polozky[{$index}][CENA_JEDNOTKA]" value="{$p->CENA_JEDNOTKA}" class="form-control item-cena text-end" required></td>
<td><input type="number" step="0.1" name="polozky[{$index}][SLEVA]" value="{$p->SLEVA}" class="form-control item-sleva text-end"></td>
<td class="text-end fw-bold item-celkem-text">{$p->CENA|number:2, ',', ' '} CZK</td>
<td class="text-center">
<td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" required></td>
<td data-label="Počet"><input type="number" step="0.01" name="polozky[{$index}][POCET]" value="{$p->POCET}" class="form-control item-pocet text-end" required></td>
<td data-label="Jedn."><input type="text" name="polozky[{$index}][JEDNOTKA]" value="{$p->JEDNOTKA}" class="form-control"></td>
<td data-label="Cena/Jedn."><input type="number" step="0.01" name="polozky[{$index}][CENA_JEDNOTKA]" value="{$p->CENA_JEDNOTKA}" class="form-control item-cena text-end" required></td>
<td data-label="Sleva %"><input type="number" step="0.1" name="polozky[{$index}][SLEVA]" value="{$p->SLEVA}" class="form-control item-sleva text-end"></td>
<td data-label="Celkem" class="text-end fw-bold item-celkem-text">{$p->CENA|number:2, ',', ' '} CZK</td>
<td class="text-center cell-actions">
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
</td>
</tr>
@ -93,9 +93,9 @@
</tbody>
<tfoot>
<tr>
<td colspan="6" class="text-end fw-bold fs-5">Celková částka:</td>
<td colspan="6" class="text-end fw-bold fs-5 cell-span">Celková částka:</td>
<td class="text-end fw-bold fs-5 text-primary" id="total-sum">0,00 CZK</td>
<td></td>
<td class="cell-actions"></td>
</tr>
</tfoot>
</table>
@ -137,7 +137,7 @@
</div>
<div class="table-responsive mb-3">
<table class="table table-sm align-middle">
<table class="table table-sm table-cards align-middle">
<thead class="table-light">
<tr>
<th style="width: 160px;">Datum</th>
@ -148,13 +148,13 @@
</thead>
<tbody>
<tr n:if="count($platby) === 0">
<td colspan="4" class="text-muted">Zatím žádná platba.</td>
<td colspan="4" class="text-muted cell-span">Zatím žádná platba.</td>
</tr>
<tr n:foreach="$form['platby']->getComponents() as $platbaId => $row">
<td>{input $row['datum'] class: 'form-control'}</td>
<td>{input $row['castka'] class: 'form-control text-end'}</td>
<td>{input $row['poznamka'] class: 'form-control'}</td>
<td class="text-end">
<td data-label="Datum">{input $row['datum'] class: 'form-control'}</td>
<td data-label="Částka">{input $row['castka'] class: 'form-control text-end'}</td>
<td data-label="Poznámka">{input $row['poznamka'] class: 'form-control'}</td>
<td class="text-end cell-actions">
<a n:href="smazatPlatbu! platbaId => $platbaId"
class="btn btn-sm btn-outline-danger"
title="Smazat platbu"
@ -164,16 +164,16 @@
</td>
</tr>
<tr>
<td>
<td data-label="Datum">
{input $form['nova']['datum'] class: 'form-control'}
</td>
<td>
<td data-label="Částka">
{input $form['nova']['castka'] class: 'form-control text-end'}
</td>
<td>
<td data-label="Poznámka">
{input $form['nova']['poznamka'] class: 'form-control', placeholder: 'Nová platba'}
</td>
<td></td>
<td class="cell-actions"></td>
</tr>
</tbody>
</table>
@ -265,16 +265,16 @@ document.addEventListener('DOMContentLoaded', () => {
const tr = document.createElement('tr');
tr.className = 'item-row';
tr.innerHTML = `
<td class="text-center align-middle drag-handle" style="cursor: grab;">
<td data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
<i class="bi bi-grip-vertical text-muted fs-5"></i>
</td>
<td><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Popis práce / zboží" required></td>
<td><input type="number" step="0.01" name="polozky[${rowIndex}][POCET]" value="1" class="form-control item-pocet text-end" required></td>
<td><input type="text" name="polozky[${rowIndex}][JEDNOTKA]" value="ks" class="form-control"></td>
<td><input type="number" step="0.01" name="polozky[${rowIndex}][CENA_JEDNOTKA]" value="0" class="form-control item-cena text-end" required></td>
<td><input type="number" step="0.1" name="polozky[${rowIndex}][SLEVA]" value="0" class="form-control item-sleva text-end"></td>
<td class="text-end fw-bold item-celkem-text">0,00 CZK</td>
<td class="text-center">
<td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Popis práce / zboží" required></td>
<td data-label="Počet"><input type="number" step="0.01" name="polozky[${rowIndex}][POCET]" value="1" class="form-control item-pocet text-end" required></td>
<td data-label="Jedn."><input type="text" name="polozky[${rowIndex}][JEDNOTKA]" value="ks" class="form-control"></td>
<td data-label="Cena/Jedn."><input type="number" step="0.01" name="polozky[${rowIndex}][CENA_JEDNOTKA]" value="0" class="form-control item-cena text-end" required></td>
<td data-label="Sleva %"><input type="number" step="0.1" name="polozky[${rowIndex}][SLEVA]" value="0" class="form-control item-sleva text-end"></td>
<td data-label="Celkem" class="text-end fw-bold item-celkem-text">0,00 CZK</td>
<td class="text-center cell-actions">
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
</td>
`;

View File

@ -44,7 +44,7 @@
<!-- Tabulka Zákazníků -->
<div class="content-card my-3">
<div class="table-responsive">
<table class="table table-hover align-middle">
<table class="table table-hover table-cards align-middle">
<thead class="table-light">
<tr>
<th>Jméno / Firma</th>
@ -58,10 +58,10 @@
<tbody>
{foreach $zakaznici as $z}
<tr n:class="!$z->AKTIVNI ? 'table-light text-muted'">
<td class="fw-bold">
<td data-label="Jméno / Firma" class="fw-bold">
{$z->PRIJMENI} {$z->JMENO}
</td>
<td>
<td data-label="Adresa">
{if $z->ULICE || $z->MESTO}
<div>{$z->ULICE}</div>
<div class="small text-muted">{$z->PSC} {$z->MESTO}</div>
@ -69,7 +69,7 @@
<span class="text-muted small">—</span>
{/if}
</td>
<td>
<td data-label="IČ / DIČ">
{if $z->IC}
<div>IČ: {$z->IC}</div>
<div class="small text-muted" n:if="$z->DIC">DIČ: {$z->DIC}</div>
@ -77,7 +77,7 @@
<span class="text-muted small">—</span>
{/if}
</td>
<td>
<td data-label="Kontakt">
{if $z->TELEFON}
<div><i class="bi bi-telephone me-1 text-muted"></i>{$z->TELEFON}</div>
{/if}
@ -88,14 +88,14 @@
<span class="text-muted small">—</span>
{/if}
</td>
<td class="text-center">
<td data-label="Stav" class="text-center">
{if $z->AKTIVNI}
<span class="badge bg-success-subtle text-success border border-success">Aktivní</span>
{else}
<span class="badge bg-secondary-subtle text-secondary border border-secondary">Neaktivní</span>
{/if}
</td>
<td class="text-end">
<td class="text-end cell-actions">
<div class="btn-group btn-group-sm" role="group">
<!-- Úprava -->
<a n:href="ZakaznikEdit:edit $z->ID"
@ -123,7 +123,7 @@
</tr>
{else}
<tr>
<td colspan="6" class="text-center py-4 text-muted">
<td colspan="6" class="text-center py-4 text-muted cell-span">
<i class="bi bi-people fs-2 d-block mb-2"></i>
Nebyli nalezeni žádní zákazníci odpovídající zadaným kritériím.
</td>

View File

@ -13,6 +13,7 @@
--card-bg: #ffffff;
--radius-sm: 0.5rem;
--radius-md: 0.75rem;
--page-max: 1080px;
--success: #14915f;
--success-hover: #0f7a4f;
--success-soft: #d9f5e8;
@ -203,19 +204,34 @@ body {
/* --- 5. OBSAH A PATKA --- */
.main-content {
flex: 1 1 auto;
padding: 1.25rem;
overflow: auto;
width: min(var(--page-max), 100%);
margin-inline: auto;
padding: 1.25rem 1rem 2rem;
}
/* Vnější obal jen drží obsah, sám se neroztahuje na výšku okna */
.main-content > .content-card {
background: transparent;
border: 0;
box-shadow: none;
height: auto;
padding: 0;
}
.content-card {
background: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
height: 100%;
height: auto;
padding: 1.25rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);
}
.main-content .container-fluid {
padding-left: 0;
padding-right: 0;
}
.app-footer {
background-color: var(--card-bg);
border-top: 1px solid var(--border-color);
@ -598,4 +614,222 @@ a:hover {
letter-spacing: 0.02em;
text-transform: uppercase;
border-bottom-color: var(--border-color);
}
.app-header > .container-fluid,
.action-toolbar > .container-fluid {
width: min(var(--page-max), 100%);
margin-inline: auto;
}
.app-footer {
padding-left: max(1.25rem, calc((100% - var(--page-max)) / 2));
padding-right: max(1.25rem, calc((100% - var(--page-max)) / 2));
}
/* --- 8. MOBIL --- */
@media (max-width: 991.98px) {
.app-header .navbar-nav {
width: 100%;
margin-top: 0.35rem;
}
.app-header .nav-link {
width: 100%;
text-align: left;
padding: 0.7rem 0.75rem;
}
.user-panel {
width: 100%;
margin: 0.35rem 0 0.25rem;
justify-content: space-between;
}
.btn-action {
flex-direction: row;
min-width: 0;
gap: 0.4rem;
padding: 0.45rem 0.7rem;
}
.btn-action i {
font-size: 1.05rem;
margin-bottom: 0;
}
.toolbar-group,
.filter-panel {
align-items: stretch;
}
.filter-panel {
width: 100%;
flex-wrap: wrap;
}
.filter-panel select {
width: auto !important;
flex: 1 1 8rem;
}
.main-content {
padding: 0.85rem 0.75rem 1.5rem;
}
.content-card {
padding: 0.9rem;
}
.main-content .d-flex.justify-content-between {
flex-wrap: wrap;
gap: 0.65rem;
}
}
@media (max-width: 767.98px) {
.dataTables_wrapper .row > [class*="col-"] {
width: 100%;
}
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
text-align: left !important;
float: none !important;
margin-bottom: 0.5rem;
}
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
width: 100%;
margin-left: 0 !important;
}
.dataTables_wrapper .dataTables_filter label,
.dataTables_wrapper .dataTables_length label {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.25rem;
width: 100%;
}
.table {
font-size: 0.82rem;
}
.table-responsive {
overflow: visible;
}
table.table-cards {
width: 100% !important;
min-width: 0 !important;
border: 0;
}
table.table-cards thead {
display: none;
}
table.table-cards tbody,
table.table-cards tfoot {
display: block;
width: 100%;
}
table.table-cards tbody tr,
table.table-cards tfoot tr {
display: block;
width: 100%;
border: 1px solid var(--border-color);
border-radius: 0.75rem;
margin-bottom: 0.75rem;
padding: 0.35rem 0.75rem 0.5rem;
background: #fff;
}
table.table-cards tbody td,
table.table-cards tfoot td {
display: flex !important;
justify-content: space-between;
align-items: center;
gap: 0.75rem;
width: auto !important;
max-width: none !important;
text-align: right !important;
white-space: normal !important;
border: 0 !important;
padding: 0.4rem 0;
}
table.table-cards tbody td::before {
content: attr(data-label);
flex: 0 0 auto;
max-width: 42%;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.02em;
text-transform: uppercase;
color: var(--text-muted);
text-align: left;
}
table.table-cards tbody td:not(:last-child) {
border-bottom: 1px solid #eef2f6 !important;
}
table.table-cards td.cell-actions {
justify-content: flex-end;
flex-wrap: wrap;
gap: 0.35rem;
padding-top: 0.55rem;
}
table.table-cards td.cell-actions::before,
table.table-cards td.cell-span::before {
content: none;
}
table.table-cards td.cell-span {
display: block !important;
text-align: center !important;
}
table.table-cards td.cell-actions .btn {
flex: 0 0 auto;
}
table.table-cards td:has(.form-control) {
flex-direction: column;
align-items: stretch;
text-align: left !important;
}
table.table-cards td:has(.form-control) .form-control {
width: 100%;
}
table.table-cards tfoot tr {
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.75rem;
}
table.table-cards tfoot td.cell-span {
display: flex !important;
text-align: left !important;
}
table.table-cards tfoot td.cell-actions:empty {
display: none !important;
}
.app-footer {
flex-wrap: wrap;
gap: 0.35rem;
}
}