PWA: Merkzettel-Box auf Produktliste, Checkboxen-Fix, qty_done=1
- PWA Panel 2: Merkzettel-Notizen über der Produktliste anzeigen (wie auf Website stundenzettel_commande.php), mit Abhaken + Hinzufügen - stundenzettel_commande.php: Grüne Haken entfernt, immer Checkboxen - Produktübernahme: qty_done immer 1 statt Auftragsmenge - Cache-Busting v2.7 → v2.8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3c9add6864
commit
8ea1180041
4 changed files with 149 additions and 12 deletions
74
css/pwa.css
74
css/pwa.css
|
|
@ -685,6 +685,80 @@ body {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === Merkzettel-Box (Panel 2, wie stundenzettel_commande.php) === */
|
||||||
|
.merkzettel-box {
|
||||||
|
background: var(--colorbackcard);
|
||||||
|
border-left: 3px solid var(--warning);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.merkzettel-box-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.merkzettel-box-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.merkzettel-box .opac {
|
||||||
|
color: var(--colortextmuted);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.merkzettel-box-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.merkzettel-box-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
.merkzettel-box-item .note-checkbox,
|
||||||
|
.merkzettel-box-item .note-checkbox-ro {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--colortextmuted);
|
||||||
|
}
|
||||||
|
.merkzettel-box-item .note-checkbox {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.merkzettel-box-item .note-checkbox.checked {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
.merkzettel-box-item .note-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.merkzettel-box-item .note-text.checked {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: var(--colortextmuted);
|
||||||
|
}
|
||||||
|
.merkzettel-box-add {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.merkzettel-box-add input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--colorbackinput);
|
||||||
|
border: 1px solid var(--colorborder);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--colortext);
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 40px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* === Tracking-Cards === */
|
/* === Tracking-Cards === */
|
||||||
.tracking-card {
|
.tracking-card {
|
||||||
background: var(--colorbackcard);
|
background: var(--colorbackcard);
|
||||||
|
|
|
||||||
68
js/pwa.js
68
js/pwa.js
|
|
@ -1058,6 +1058,52 @@
|
||||||
var canWrite = self.state.canWrite;
|
var canWrite = self.state.canWrite;
|
||||||
var hasSelectable = false;
|
var hasSelectable = false;
|
||||||
var activeFilter = self.state.productFilter || 'open';
|
var activeFilter = self.state.productFilter || 'open';
|
||||||
|
var isDraft = stz && stz.status == 0;
|
||||||
|
|
||||||
|
// Merkzettel-Notizen oben anzeigen (wie stundenzettel_commande.php)
|
||||||
|
if (self.data.notes && self.data.notes.length) {
|
||||||
|
html += '<div class="merkzettel-box">';
|
||||||
|
html += '<div class="merkzettel-box-header">';
|
||||||
|
html += '<span class="merkzettel-box-icon">📋</span>';
|
||||||
|
html += '<strong>Merkzettel</strong>';
|
||||||
|
if (stz) html += ' <span class="opac">(' + self.escHtml(stz.ref) + ')</span>';
|
||||||
|
html += '</div>';
|
||||||
|
html += '<ul class="merkzettel-box-list">';
|
||||||
|
self.data.notes.forEach(function(n) {
|
||||||
|
var checked = n.checked == 1;
|
||||||
|
html += '<li class="merkzettel-box-item">';
|
||||||
|
if (isDraft && canWrite) {
|
||||||
|
html += '<span class="note-checkbox' + (checked ? ' checked' : '') + '" data-id="' + n.id + '" data-checked="' + (checked ? 0 : 1) + '">';
|
||||||
|
html += checked ? '☑' : '☐';
|
||||||
|
html += '</span>';
|
||||||
|
} else {
|
||||||
|
html += '<span class="note-checkbox-ro">' + (checked ? '☑' : '☐') + '</span>';
|
||||||
|
}
|
||||||
|
html += '<span class="' + (checked ? 'note-text checked' : 'note-text') + '">' + self.escHtml(n.note) + '</span>';
|
||||||
|
html += '</li>';
|
||||||
|
});
|
||||||
|
html += '</ul>';
|
||||||
|
// Notiz-Input (nur im Entwurf)
|
||||||
|
if (isDraft && canWrite) {
|
||||||
|
html += '<div class="merkzettel-box-add">';
|
||||||
|
html += '<input type="text" id="note-input-p2" placeholder="Neue Notiz...">';
|
||||||
|
html += '<button class="btn btn-primary btn-icon" id="btn-add-note-p2">+</button>';
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
} else if (isDraft && canWrite) {
|
||||||
|
// Leere Box mit nur Input zum Hinzufuegen
|
||||||
|
html += '<div class="merkzettel-box">';
|
||||||
|
html += '<div class="merkzettel-box-header">';
|
||||||
|
html += '<span class="merkzettel-box-icon">📋</span>';
|
||||||
|
html += '<strong>Merkzettel</strong>';
|
||||||
|
html += '</div>';
|
||||||
|
html += '<div class="merkzettel-box-add">';
|
||||||
|
html += '<input type="text" id="note-input-p2" placeholder="Neue Notiz...">';
|
||||||
|
html += '<button class="btn btn-primary btn-icon" id="btn-add-note-p2">+</button>';
|
||||||
|
html += '</div>';
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
// Filter-Buttons (wie Desktop: Offen/Erledigt/Alle)
|
// Filter-Buttons (wie Desktop: Offen/Erledigt/Alle)
|
||||||
html += '<div class="filter-bar">';
|
html += '<div class="filter-bar">';
|
||||||
|
|
@ -1248,6 +1294,13 @@
|
||||||
$panel.find('.btn-create-new-stz').on('click', function() {
|
$panel.find('.btn-create-new-stz').on('click', function() {
|
||||||
self.showCreateStzDialog();
|
self.showCreateStzDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Merkzettel auf Panel 2: Abhaken + Hinzufuegen
|
||||||
|
$panel.find('.merkzettel-box .note-checkbox').on('click', function() {
|
||||||
|
self.toggleNote($(this).data('id'), $(this).data('checked'));
|
||||||
|
});
|
||||||
|
$('#btn-add-note-p2').on('click', function() { self.addNoteFromPanel2(); });
|
||||||
|
$('#note-input-p2').on('keypress', function(e) { if (e.which === 13) self.addNoteFromPanel2(); });
|
||||||
},
|
},
|
||||||
|
|
||||||
renderProductCard: function(p, isDraft, canWrite) {
|
renderProductCard: function(p, isDraft, canWrite) {
|
||||||
|
|
@ -1664,6 +1717,21 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addNoteFromPanel2: function() {
|
||||||
|
var self = this;
|
||||||
|
var text = $('#note-input-p2').val().trim();
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
self.api('add_note', {stz_id: self.state.stzId, note: text}).then(function(res) {
|
||||||
|
if (res.success) {
|
||||||
|
$('#note-input-p2').val('');
|
||||||
|
self.reloadData();
|
||||||
|
} else {
|
||||||
|
self.showToast(res.error || 'Fehler', 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
toggleNote: function(id, checked) {
|
toggleNote: function(id, checked) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.api('toggle_note', {note_id: id, checked: checked, stz_id: self.state.stzId}).then(function(res) {
|
self.api('toggle_note', {note_id: id, checked: checked, stz_id: self.state.stzId}).then(function(res) {
|
||||||
|
|
|
||||||
4
pwa.php
4
pwa.php
|
|
@ -38,7 +38,7 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#4390dc');
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="img/icon-192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="img/icon-192.png">
|
||||||
<link rel="apple-touch-icon" href="img/icon-192.png">
|
<link rel="apple-touch-icon" href="img/icon-192.png">
|
||||||
<link rel="stylesheet" href="css/pwa.css?v=2.7">
|
<link rel="stylesheet" href="css/pwa.css?v=2.8">
|
||||||
<style>:root { --primary: <?php echo htmlspecialchars($themeColor); ?>; }</style>
|
<style>:root { --primary: <?php echo htmlspecialchars($themeColor); ?>; }</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -160,7 +160,7 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#4390dc');
|
||||||
authUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_auth.php', 1); ?>'
|
authUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_auth.php', 1); ?>'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="js/pwa.js?v=2.7"></script>
|
<script src="js/pwa.js?v=2.8"></script>
|
||||||
|
|
||||||
<!-- Service Worker Registration -->
|
<!-- Service Worker Registration -->
|
||||||
<script>
|
<script>
|
||||||
|
|
|
||||||
|
|
@ -175,13 +175,13 @@ if ($action == 'transfer_products' && $user->hasRight('stundenzettel', 'write'))
|
||||||
$sql2 .= " AND fk_commandedet = ".((int)$line_id);
|
$sql2 .= " AND fk_commandedet = ".((int)$line_id);
|
||||||
$resql2 = $db->query($sql2);
|
$resql2 = $db->query($sql2);
|
||||||
if ($resql2 && $db->num_rows($resql2) == 0) {
|
if ($resql2 && $db->num_rows($resql2) == 0) {
|
||||||
// Noch nicht vorhanden, hinzufügen
|
// Noch nicht vorhanden, hinzufügen (immer mit Menge 1)
|
||||||
$stundenzettel->addProduct(
|
$stundenzettel->addProduct(
|
||||||
$obj->fk_product,
|
$obj->fk_product,
|
||||||
$obj->rowid,
|
$obj->rowid,
|
||||||
null,
|
null,
|
||||||
$obj->qty,
|
$obj->qty,
|
||||||
0,
|
1,
|
||||||
'order',
|
'order',
|
||||||
$obj->description // Beschreibung für Freitext-Produkte
|
$obj->description // Beschreibung für Freitext-Produkte
|
||||||
);
|
);
|
||||||
|
|
@ -206,7 +206,7 @@ if ($action == 'transfer_products' && $user->hasRight('stundenzettel', 'write'))
|
||||||
$resqlMa = $db->query($sqlMa);
|
$resqlMa = $db->query($sqlMa);
|
||||||
|
|
||||||
if ($resqlMa && ($objMa = $db->fetch_object($resqlMa))) {
|
if ($resqlMa && ($objMa = $db->fetch_object($resqlMa))) {
|
||||||
$qty = max(1, (float)$objMa->qty_done); // Mindestens 1
|
$qty = 1; // Immer Menge 1 uebernehmen
|
||||||
|
|
||||||
// Prüfe ob Produkt schon auf diesem Stundenzettel (in Produktliste) existiert
|
// Prüfe ob Produkt schon auf diesem Stundenzettel (in Produktliste) existiert
|
||||||
$sqlCheck = "SELECT rowid, qty_done FROM ".MAIN_DB_PREFIX."stundenzettel_product";
|
$sqlCheck = "SELECT rowid, qty_done FROM ".MAIN_DB_PREFIX."stundenzettel_product";
|
||||||
|
|
@ -1865,14 +1865,9 @@ if ($tab == 'products') {
|
||||||
|
|
||||||
print '<tr class="oddeven'.$sectionClass.'">';
|
print '<tr class="oddeven'.$sectionClass.'">';
|
||||||
|
|
||||||
// Checkbox immer anzeigen, ausser wenn bereits auf diesem Stundenzettel
|
// Checkbox immer anzeigen
|
||||||
$isAlreadyOnStz = isset($alreadyOnStundenzettel[$obj->rowid]);
|
|
||||||
print '<td class="center"'.$styleFirst.'>';
|
print '<td class="center"'.$styleFirst.'>';
|
||||||
if (!$isAlreadyOnStz) {
|
print '<input type="checkbox" name="selected[]" value="'.$obj->rowid.'" class="product-checkbox">';
|
||||||
print '<input type="checkbox" name="selected[]" value="'.$obj->rowid.'" class="product-checkbox">';
|
|
||||||
} else {
|
|
||||||
print '<span class="fas fa-check" style="color: #28a745;" title="'.$langs->trans("AlreadyOnStundenzettel").'"></span>';
|
|
||||||
}
|
|
||||||
print '</td>';
|
print '</td>';
|
||||||
|
|
||||||
// Produkt
|
// Produkt
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue