Mobile UX: Formulare und Anzeige optimiert
- Beschreibungsfelder bei Leistung/Mehraufwand/Entfällt auf separate Zeile für Mobile (bessere Bedienbarkeit) - Grund/Beschreibung wird auf Mobile klein unter dem Produktnamen angezeigt (spart Platz) - Spalte "Beschreibung" auf Mobile ausgeblendet - Action-Buttons sticky am unteren Bildschirmrand - Tabs horizontal scrollbar statt umbruch - Verbesserte Touch-Targets und Abstände Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2b3514f762
commit
d0e867aba3
2 changed files with 154 additions and 40 deletions
69
card.php
69
card.php
|
|
@ -946,7 +946,7 @@ elseif ($object->id > 0) {
|
|||
print '<th>'.$langs->trans("LeistungTimeEnd").'</th>';
|
||||
print '<th class="center">'.$langs->trans("Duration").'</th>';
|
||||
print '<th>'.$langs->trans("DefaultService").'</th>';
|
||||
print '<th>'.$langs->trans("Description").'</th>';
|
||||
print '<th class="mobile-hide">'.$langs->trans("Description").'</th>';
|
||||
if ($object->status == Stundenzettel::STATUS_DRAFT && $permissiontoadd) {
|
||||
print '<th class="center" width="40"></th>'; // Edit
|
||||
print '<th class="center" width="40"></th>'; // Delete
|
||||
|
|
@ -1009,8 +1009,13 @@ elseif ($object->id > 0) {
|
|||
} else {
|
||||
print '<span class="opacitymedium">'.$langs->trans("NotSet").'</span>';
|
||||
}
|
||||
// Mobile: Beschreibung unter Leistungsposition anzeigen
|
||||
if (!empty($leistung->description)) {
|
||||
print '<div class="mobile-inline-desc"><small class="opacitymedium">'.dol_trunc(strip_tags($leistung->description), 60).'</small></div>';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td>'.dol_htmlentitiesbr($leistung->description).'</td>';
|
||||
// Beschreibung (auf Mobile ausgeblendet)
|
||||
print '<td class="mobile-hide">'.dol_htmlentitiesbr($leistung->description).'</td>';
|
||||
if ($object->status == Stundenzettel::STATUS_DRAFT && $permissiontoadd) {
|
||||
// Edit Button
|
||||
print '<td class="center">';
|
||||
|
|
@ -1081,9 +1086,9 @@ elseif ($object->id > 0) {
|
|||
$form->select_produits($defaultServiceId, 'fk_product', 1, 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'maxwidth200');
|
||||
print '</td>';
|
||||
|
||||
// Beschreibung (Tagesbeschreibung - größeres Textarea, 5 Zeilen)
|
||||
print '<td>';
|
||||
print '<textarea name="leistung_description" class="flat" rows="5" style="width: 300px; resize: vertical;" placeholder="'.$langs->trans("Description").'"></textarea>';
|
||||
// Beschreibung (Desktop: in Zeile, Mobile: ausgeblendet)
|
||||
print '<td class="mobile-hide">';
|
||||
print '<textarea name="leistung_description" class="flat" rows="3" style="width: 200px; resize: vertical;" placeholder="'.$langs->trans("Description").'"></textarea>';
|
||||
print '</td>';
|
||||
|
||||
// Action (colspan=2 für beide Button-Spalten)
|
||||
|
|
@ -1093,6 +1098,13 @@ elseif ($object->id > 0) {
|
|||
|
||||
print '</form>';
|
||||
print '</tr>';
|
||||
|
||||
// Mobile: Beschreibung in separater Zeile
|
||||
print '<tr class="oddeven mobile-description-row">';
|
||||
print '<td colspan="8">';
|
||||
print '<textarea name="leistung_description" form="form_leistung" class="flat" rows="3" style="width: 100%; resize: vertical;" placeholder="'.$langs->trans("Description").'"></textarea>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
// Summenzeile anzeigen
|
||||
|
|
@ -1332,7 +1344,7 @@ elseif ($object->id > 0) {
|
|||
print '<tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans("Product").'</th>';
|
||||
print '<th class="center" style="width:80px;">'.$langs->trans("Qty").'</th>';
|
||||
print '<th style="width:200px;">'.$langs->trans("Reason").'</th>';
|
||||
print '<th class="mobile-hide" style="width:200px;">'.$langs->trans("Reason").'</th>';
|
||||
print '<th class="center" style="width:40px;"></th>'; // Save
|
||||
print '<th class="center" style="width:40px;"></th>'; // Delete
|
||||
print '</tr>';
|
||||
|
|
@ -1365,6 +1377,10 @@ elseif ($object->id > 0) {
|
|||
print '<span class="opacitymedium">'.$displayText.'</span>';
|
||||
}
|
||||
print ' <span class="badge badge-secondary">'.$langs->trans("Entfaellt").'</span>';
|
||||
// Mobile: Grund unter Produktname anzeigen
|
||||
if (!empty($prod->description)) {
|
||||
print '<div class="mobile-inline-desc"><small class="opacitymedium">'.dol_trunc(strip_tags($prod->description), 50).'</small></div>';
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Menge
|
||||
|
|
@ -1377,8 +1393,8 @@ elseif ($object->id > 0) {
|
|||
print '</form>';
|
||||
print '</td>';
|
||||
|
||||
// Grund/Beschreibung
|
||||
print '<td style="width:200px;">';
|
||||
// Grund/Beschreibung (auf Mobile ausgeblendet)
|
||||
print '<td class="mobile-hide" style="width:200px;">';
|
||||
if (!empty($prod->description)) {
|
||||
print dol_htmlentitiesbr($prod->description);
|
||||
} else {
|
||||
|
|
@ -1448,7 +1464,7 @@ elseif ($object->id > 0) {
|
|||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products">';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products" id="form_entfaellt">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="add_entfaellt">';
|
||||
|
||||
|
|
@ -1534,8 +1550,8 @@ elseif ($object->id > 0) {
|
|||
print '<input type="number" name="entfaellt_qty" id="entfaellt_qty_input" class="flat" style="width:70px; text-align:center;" value="1" min="1">';
|
||||
print '</td>';
|
||||
|
||||
// Grund
|
||||
print '<td style="width:200px;">';
|
||||
// Grund (Desktop: in Zeile, Mobile: ausgeblendet)
|
||||
print '<td class="mobile-hide" style="width:200px;">';
|
||||
print '<input type="text" name="entfaellt_description" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
|
||||
print '</td>';
|
||||
|
||||
|
|
@ -1549,6 +1565,13 @@ elseif ($object->id > 0) {
|
|||
print '</form>';
|
||||
print '</tr>';
|
||||
|
||||
// Mobile: Grund in separater Zeile
|
||||
print '<tr class="oddeven mobile-description-row">';
|
||||
print '<td colspan="5">';
|
||||
print '<input type="text" name="entfaellt_description" form="form_entfaellt" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// JavaScript für dynamische Max-Menge
|
||||
print '<script>
|
||||
function updateMaxQty(selectElement) {
|
||||
|
|
@ -1585,7 +1608,7 @@ elseif ($object->id > 0) {
|
|||
print '<tr class="liste_titre">';
|
||||
print '<th>'.$langs->trans("Product").'</th>';
|
||||
print '<th class="center" style="width:80px;">'.$langs->trans("Qty").'</th>';
|
||||
print '<th style="width:200px;">'.$langs->trans("Reason").'</th>';
|
||||
print '<th class="mobile-hide" style="width:200px;">'.$langs->trans("Reason").'</th>';
|
||||
print '<th class="center" style="width:40px;"></th>'; // Save
|
||||
print '<th class="center" style="width:40px;"></th>'; // Delete
|
||||
print '</tr>';
|
||||
|
|
@ -1620,6 +1643,11 @@ elseif ($object->id > 0) {
|
|||
print '<span class="opacitymedium">'.$displayText.'</span>';
|
||||
}
|
||||
print ' <span class="badge badge-warning">'.$langs->trans("Mehraufwand").'</span>';
|
||||
// Mobile: Grund unter Produktname anzeigen
|
||||
$reason = ($prod->fk_product > 0) ? $prod->description : '';
|
||||
if (!empty($reason)) {
|
||||
print '<div class="mobile-inline-desc"><small class="opacitymedium">'.dol_trunc(strip_tags($reason), 50).'</small></div>';
|
||||
}
|
||||
print '</td>';
|
||||
|
||||
// Menge (qty_done) - editierbar
|
||||
|
|
@ -1635,8 +1663,8 @@ elseif ($object->id > 0) {
|
|||
}
|
||||
print '</td>';
|
||||
|
||||
// Grund (nur wenn Produkt gesetzt, sonst ist description der Produktname)
|
||||
print '<td style="width:200px;">';
|
||||
// Grund (auf Mobile ausgeblendet, nur wenn Produkt gesetzt)
|
||||
print '<td class="mobile-hide" style="width:200px;">';
|
||||
if ($object->status == Stundenzettel::STATUS_DRAFT && $permissiontoadd) {
|
||||
// Grund nur anzeigen/editieren wenn ein Produkt gewählt wurde
|
||||
$reason = ($prod->fk_product > 0) ? $prod->description : '';
|
||||
|
|
@ -1680,7 +1708,7 @@ elseif ($object->id > 0) {
|
|||
print '</tr>';
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products">';
|
||||
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products" id="form_mehraufwand">';
|
||||
print '<input type="hidden" name="token" value="'.newToken().'">';
|
||||
print '<input type="hidden" name="action" value="add_mehraufwand">';
|
||||
|
||||
|
|
@ -1696,8 +1724,8 @@ elseif ($object->id > 0) {
|
|||
print '<input type="number" name="mehraufwand_qty" class="flat" style="width:70px; text-align:center;" value="1" min="1">';
|
||||
print '</td>';
|
||||
|
||||
// Grund
|
||||
print '<td style="width:200px;">';
|
||||
// Grund (Desktop: in Zeile, Mobile: ausgeblendet)
|
||||
print '<td class="mobile-hide" style="width:200px;">';
|
||||
print '<input type="text" name="mehraufwand_reason" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
|
||||
print '</td>';
|
||||
|
||||
|
|
@ -1710,6 +1738,13 @@ elseif ($object->id > 0) {
|
|||
|
||||
print '</form>';
|
||||
print '</tr>';
|
||||
|
||||
// Mobile: Grund in separater Zeile
|
||||
print '<tr class="oddeven mobile-description-row">';
|
||||
print '<td colspan="5">';
|
||||
print '<input type="text" name="mehraufwand_reason" form="form_mehraufwand" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,20 @@
|
|||
/**
|
||||
* Stundenzettel Mobile CSS
|
||||
* Responsive Styles für Touch-Geräte
|
||||
* Version 1.2.0
|
||||
* Version 1.2.1
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
DESKTOP: Mobile-Elemente ausblenden
|
||||
============================================ */
|
||||
.mod-stundenzettel .mobile-description-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mod-stundenzettel .mobile-inline-desc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
MOBILE STYLES (max-width: 768px)
|
||||
============================================ */
|
||||
|
|
@ -78,10 +89,10 @@
|
|||
padding: 8px 4px !important;
|
||||
}
|
||||
|
||||
/* Zeit-Inputs */
|
||||
/* Zeit-Inputs kompakter */
|
||||
.mod-stundenzettel input[type="time"] {
|
||||
width: 80px !important;
|
||||
padding: 8px !important;
|
||||
width: 70px !important;
|
||||
padding: 6px 4px !important;
|
||||
}
|
||||
|
||||
/* Textarea volle Breite */
|
||||
|
|
@ -98,7 +109,7 @@
|
|||
|
||||
.mod-stundenzettel select.minwidth200,
|
||||
.mod-stundenzettel select.minwidth300 {
|
||||
min-width: 150px !important;
|
||||
min-width: 120px !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
|
|
@ -109,10 +120,10 @@
|
|||
/* Icons größer und mit Padding für Touch */
|
||||
.mod-stundenzettel td .fas,
|
||||
.mod-stundenzettel td .far {
|
||||
font-size: 1.3em;
|
||||
padding: 8px;
|
||||
min-width: 35px;
|
||||
min-height: 35px;
|
||||
font-size: 1.2em;
|
||||
padding: 6px;
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -125,31 +136,35 @@
|
|||
}
|
||||
|
||||
/* ============================================
|
||||
TABS - Kompakter
|
||||
TABS - Kompakter und horizontal scrollbar
|
||||
============================================ */
|
||||
.mod-stundenzettel .tabs {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.mod-stundenzettel .tabsElem a {
|
||||
padding: 8px 10px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
/* Tabs umbrechen wenn nötig */
|
||||
.mod-stundenzettel .tabs {
|
||||
flex-wrap: wrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
SECTION-TITEL
|
||||
============================================ */
|
||||
.mod-stundenzettel h3 {
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
padding: 8px 10px;
|
||||
margin: 12px 0 8px 0;
|
||||
margin: 10px 0 6px 0;
|
||||
}
|
||||
|
||||
/* Abschnitts-Header in Tabellen */
|
||||
.mod-stundenzettel tr.liste_titre th[colspan] {
|
||||
font-size: 14px;
|
||||
padding: 10px 8px !important;
|
||||
font-size: 13px;
|
||||
padding: 8px 6px !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
|
|
@ -160,14 +175,19 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 10px 5px;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
z-index: 100;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.mod-stundenzettel .tabsAction a.butAction,
|
||||
.mod-stundenzettel .tabsAction a.butActionDelete {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
text-align: center;
|
||||
padding: 12px 8px !important;
|
||||
font-size: 13px !important;
|
||||
padding: 14px 10px !important;
|
||||
font-size: 14px !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
@ -175,11 +195,16 @@
|
|||
BANNER/HEADER - Kompakter
|
||||
============================================ */
|
||||
.mod-stundenzettel .arearef {
|
||||
padding: 8px !important;
|
||||
padding: 6px !important;
|
||||
}
|
||||
|
||||
.mod-stundenzettel .refid {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Banner-Tabelle kompakter */
|
||||
.mod-stundenzettel .refidno {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
|
|
@ -195,6 +220,7 @@
|
|||
|
||||
.mod-stundenzettel .select2-selection__rendered {
|
||||
line-height: 38px !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
|
|
@ -232,6 +258,59 @@
|
|||
.mod-stundenzettel tr.liste_total td {
|
||||
font-size: 13px;
|
||||
padding: 8px 4px !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
LEISTUNGEN TABELLE - Spalten optimieren
|
||||
============================================ */
|
||||
/* Beschreibungs-Spalte kompakter (Wörter umbrechen) */
|
||||
.mod-stundenzettel table.noborder td:nth-child(6) {
|
||||
max-width: 100px;
|
||||
word-break: break-word;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Textarea in Edit-Mode kompakter */
|
||||
.mod-stundenzettel tr.oddeven textarea {
|
||||
min-height: 50px;
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
MOBILE FORMULARE - Separate Beschreibungszeile
|
||||
============================================ */
|
||||
/* Mobile-Zeilen anzeigen */
|
||||
.mod-stundenzettel .mobile-description-row {
|
||||
display: table-row;
|
||||
background: #f8f9fa !important;
|
||||
}
|
||||
|
||||
.mod-stundenzettel .mobile-description-row td {
|
||||
padding: 8px !important;
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.mod-stundenzettel .mobile-description-row input,
|
||||
.mod-stundenzettel .mobile-description-row textarea {
|
||||
width: 100% !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Desktop-Spalten auf Mobile ausblenden */
|
||||
.mod-stundenzettel .mobile-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
INLINE BESCHREIBUNG (unter Produktname)
|
||||
============================================ */
|
||||
.mod-stundenzettel .mobile-inline-desc {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
line-height: 1.3;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue