Compare commits

..

No commits in common. "192cdad8e08703a437b52bea527e2b36da309af0" and "2b3514f762d6c84ae67c0f8ec74f90127305bd07" have entirely different histories.

3 changed files with 43 additions and 177 deletions

View file

@ -102,13 +102,10 @@ Sie können beim Kunden (unter **Kunden > Kundenkarte**) eine Standard-Leistung
### Version 1.2.0
- **Leistungsposition pro Arbeitszeit**: Jede Arbeitszeit kann einer eigenen Leistungsposition (Dienstleistung) zugeordnet werden
- **Mobile-optimierte Ansicht**: Responsive CSS für Touch-Geräte (Smartphones/Tablets)
- Kompakte Tabellen mit horizontalem Scrollen bei Bedarf
- Größere Touch-Targets für Buttons und Icons
- Automatisches Karten-Layout bei Bildschirmbreite < 768px
- Größere Touch-Buttons (44px+)
- Kein horizontales Scrollen mehr
- Optimierte Eingabefelder (Font 16px, kein iOS-Zoom)
- Beschreibungsfelder auf separater Zeile für bessere Bedienbarkeit
- Grund/Beschreibung wird klein unter dem Produktnamen angezeigt
- Action-Buttons sticky am unteren Bildschirmrand
- Dark Mode Support (kompatibel mit Dolibarr Themes)
- **Erweiterte Lieferauflistung**: Tracking-Tab zeigt jetzt auch Leistungen gruppiert nach Leistungsposition
- **Verbesserte Tab-Navigation**: Einheitliche Tabs auf Auftrags- und Stundenzettel-Ebene
- **Layout-Verbesserungen**: Konsistente Spaltenbreiten und Ausrichtung in allen Tabellen

View file

@ -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 class="mobile-hide">'.$langs->trans("Description").'</th>';
print '<th>'.$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,13 +1009,8 @@ 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>';
// Beschreibung (auf Mobile ausgeblendet)
print '<td class="mobile-hide">'.dol_htmlentitiesbr($leistung->description).'</td>';
print '<td>'.dol_htmlentitiesbr($leistung->description).'</td>';
if ($object->status == Stundenzettel::STATUS_DRAFT && $permissiontoadd) {
// Edit Button
print '<td class="center">';
@ -1086,9 +1081,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 (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>';
// 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>';
print '</td>';
// Action (colspan=2 für beide Button-Spalten)
@ -1098,13 +1093,6 @@ 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
@ -1344,7 +1332,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 class="mobile-hide" style="width:200px;">'.$langs->trans("Reason").'</th>';
print '<th 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>';
@ -1377,10 +1365,6 @@ 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
@ -1393,8 +1377,8 @@ elseif ($object->id > 0) {
print '</form>';
print '</td>';
// Grund/Beschreibung (auf Mobile ausgeblendet)
print '<td class="mobile-hide" style="width:200px;">';
// Grund/Beschreibung
print '<td style="width:200px;">';
if (!empty($prod->description)) {
print dol_htmlentitiesbr($prod->description);
} else {
@ -1464,7 +1448,7 @@ elseif ($object->id > 0) {
print '</tr>';
print '<tr class="oddeven">';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products" id="form_entfaellt">';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="add_entfaellt">';
@ -1550,8 +1534,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 (Desktop: in Zeile, Mobile: ausgeblendet)
print '<td class="mobile-hide" style="width:200px;">';
// Grund
print '<td style="width:200px;">';
print '<input type="text" name="entfaellt_description" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
print '</td>';
@ -1565,13 +1549,6 @@ 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) {
@ -1608,7 +1585,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 class="mobile-hide" style="width:200px;">'.$langs->trans("Reason").'</th>';
print '<th 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>';
@ -1643,11 +1620,6 @@ 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
@ -1663,8 +1635,8 @@ elseif ($object->id > 0) {
}
print '</td>';
// Grund (auf Mobile ausgeblendet, nur wenn Produkt gesetzt)
print '<td class="mobile-hide" style="width:200px;">';
// Grund (nur wenn Produkt gesetzt, sonst ist description der Produktname)
print '<td 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 : '';
@ -1708,7 +1680,7 @@ elseif ($object->id > 0) {
print '</tr>';
print '<tr class="oddeven">';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products" id="form_mehraufwand">';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'?id='.$object->id.'&tab=products">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="add_mehraufwand">';
@ -1724,8 +1696,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 (Desktop: in Zeile, Mobile: ausgeblendet)
print '<td class="mobile-hide" style="width:200px;">';
// Grund
print '<td style="width:200px;">';
print '<input type="text" name="mehraufwand_reason" class="flat" style="width:100%;" placeholder="'.$langs->trans("Reason").'">';
print '</td>';
@ -1738,13 +1710,6 @@ 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>';

View file

@ -1,20 +1,9 @@
/**
* Stundenzettel Mobile CSS
* Responsive Styles für Touch-Geräte
* Version 1.2.1
* Version 1.2.0
*/
/* ============================================
DESKTOP: Mobile-Elemente ausblenden
============================================ */
.mod-stundenzettel .mobile-description-row {
display: none;
}
.mod-stundenzettel .mobile-inline-desc {
display: none;
}
/* ============================================
MOBILE STYLES (max-width: 768px)
============================================ */
@ -89,10 +78,10 @@
padding: 8px 4px !important;
}
/* Zeit-Inputs kompakter */
/* Zeit-Inputs */
.mod-stundenzettel input[type="time"] {
width: 70px !important;
padding: 6px 4px !important;
width: 80px !important;
padding: 8px !important;
}
/* Textarea volle Breite */
@ -109,7 +98,7 @@
.mod-stundenzettel select.minwidth200,
.mod-stundenzettel select.minwidth300 {
min-width: 120px !important;
min-width: 150px !important;
max-width: 100% !important;
}
@ -120,10 +109,10 @@
/* Icons größer und mit Padding für Touch */
.mod-stundenzettel td .fas,
.mod-stundenzettel td .far {
font-size: 1.2em;
padding: 6px;
min-width: 32px;
min-height: 32px;
font-size: 1.3em;
padding: 8px;
min-width: 35px;
min-height: 35px;
display: inline-flex;
align-items: center;
justify-content: center;
@ -136,35 +125,31 @@
}
/* ============================================
TABS - Kompakter und horizontal scrollbar
TABS - Kompakter
============================================ */
.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;
white-space: nowrap;
}
/* Tabs umbrechen wenn nötig */
.mod-stundenzettel .tabs {
flex-wrap: wrap;
}
/* ============================================
SECTION-TITEL
============================================ */
.mod-stundenzettel h3 {
font-size: 15px;
font-size: 16px;
padding: 8px 10px;
margin: 10px 0 6px 0;
margin: 12px 0 8px 0;
}
/* Abschnitts-Header in Tabellen */
.mod-stundenzettel tr.liste_titre th[colspan] {
font-size: 13px;
padding: 8px 6px !important;
font-size: 14px;
padding: 10px 8px !important;
}
/* ============================================
@ -175,19 +160,14 @@
flex-wrap: wrap;
gap: 8px;
padding: 10px 5px;
position: sticky;
bottom: 0;
background: var(--colorbackbody, inherit);
z-index: 100;
box-shadow: 0 -2px 8px rgba(0,0,0,0.15);
}
.mod-stundenzettel .tabsAction a.butAction,
.mod-stundenzettel .tabsAction a.butActionDelete {
flex: 1 1 calc(50% - 8px);
text-align: center;
padding: 14px 10px !important;
font-size: 14px !important;
padding: 12px 8px !important;
font-size: 13px !important;
white-space: nowrap;
}
@ -195,16 +175,11 @@
BANNER/HEADER - Kompakter
============================================ */
.mod-stundenzettel .arearef {
padding: 6px !important;
padding: 8px !important;
}
.mod-stundenzettel .refid {
font-size: 13px;
}
/* Banner-Tabelle kompakter */
.mod-stundenzettel .refidno {
font-size: 12px;
font-size: 14px;
}
/* ============================================
@ -220,7 +195,6 @@
.mod-stundenzettel .select2-selection__rendered {
line-height: 38px !important;
font-size: 14px !important;
}
/* ============================================
@ -258,59 +232,6 @@
.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 (aber nicht in mobile-description-row) */
.mod-stundenzettel tr.oddeven:not(.mobile-description-row) textarea {
min-height: 50px;
width: 150px !important;
}
/* ============================================
MOBILE FORMULARE - Separate Beschreibungszeile
============================================ */
/* Mobile-Zeilen anzeigen */
.mod-stundenzettel .mobile-description-row {
display: table-row;
background: var(--colorbacklineimpair2, inherit) !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;
opacity: 0.7;
}
}
@ -365,20 +286,3 @@
transform: scale(1.2);
}
}
/* ============================================
DARK MODE SUPPORT
============================================ */
@media (prefers-color-scheme: dark) {
/* Nur anwenden wenn Dolibarr Dark Theme aktiv (body hat dark-Klasse) */
body.theme-eldy-dark .mod-stundenzettel .tabsAction,
body[class*="dark"] .mod-stundenzettel .tabsAction {
background: var(--colorbackbody, #1e1e1e);
box-shadow: 0 -2px 8px rgba(0,0,0,0.4);
}
body.theme-eldy-dark .mod-stundenzettel .mobile-description-row,
body[class*="dark"] .mod-stundenzettel .mobile-description-row {
background: var(--colorbacklineimpair2, #2d2d2d) !important;
}
}