Bas 64 Part ans ende der Source verschoben

This commit is contained in:
Florian Bernard committed 2026-08-28 11:38:12 +00:00
1 parent ef72744776
commit 037669567e
1 file changed
+2 -2
+2 -2
View File
@@ -59,7 +59,7 @@
"srcDoc": {
"css": "@charset \"UTF-8\";\nhtml,\nbody {\n margin: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n font-family: Arial, sans-serif;\n}\n\n/* Bild + Overlay */\n#imgHolder {\n position: relative;\n width: 100%;\n height: 700px; /* feste Höhe, damit sicher nicht 0 */\n overflow: hidden;\n border-radius: 6px;\n background: #f3f4f6;\n}\n\n#floorplanImage {\n position: absolute;\n inset: 0;\n z-index: 1;\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n#seatLayer {\n position: absolute;\n inset: 0;\n z-index: 2;\n width: 100%;\n height: 100%;\n pointer-events: none; /* Punkte bekommen später pointer-events:auto */\n}\n\n/* Sitzpunkte */\n.seat {\n position: absolute;\n z-index: 3;\n display: block;\n width: 18px;\n height: 18px;\n padding: 0;\n transform: translate(-50%, -50%);\n border: 2px solid #ffffff;\n border-radius: 50%;\n background: #22c55e;\n color: #22c55e;\n cursor: pointer;\n pointer-events: auto;\n appearance: none;\n box-sizing: border-box;\n box-shadow: 0 0 5px currentColor, 0 0 10px currentColor;\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.seat:hover {\n z-index: 10;\n transform: translate(-50%, -50%) scale(1.45);\n box-shadow: 0 0 8px currentColor, 0 0 16px currentColor;\n}\n\n/* Farben nach Status */\n.seat.frei {\n background: #22c55e; /* Grün */\n color: #22c55e;\n}\n\n.seat.belegt {\n background: #ef4444; /* Rot */\n color: #ef4444;\n}\n\n.seat.selected {\n outline: 3px solid #2563eb;\n outline-offset: 3px;\n}\n\n/* Legende */\n#legend {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n margin-top: 8px;\n color: #374151;\n font-size: 13px;\n}\n\n.legendItem {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.legendColor {\n display: inline-block;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n}\n\n.legendColor.frei {\n background: #22c55e;\n}\n\n.legendColor.belegt {\n background: #ef4444;\n}",
"html": "<div id=\"seatPlan\">\n\n <div id=\"imgHolder\">\n <!-- Kein src hier, das Bild kommt über JavaScript -->\n <img id=\"floorplanImage\" alt=\"Büroplan\">\n\t\t<div id=\"seatLayer\"></div>\n\t\t\n\t\t\n </div>\n</div>",
"js": "let selectedSeat = null;\n\n/**\n * Base64-Bild: WICHTIG – Typ muss zum Inhalt passen!\n * Wenn dein String mit \"iVBORw0KGgo...\" beginnt, ist das PNG:\n */\nconst FLOORPLAN_IMAGE = \"data:image/jpeg;base64,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 truncated
"js": "let selectedSeat = null;\n\n/**\n * Model aus Appsmith holen\n */\nfunction getModel() {\n const model = appsmith.model || {};\n if (typeof model === \"string\") {\n try {\n return JSON.parse(model);\n } catch (error) {\n return {};\n }\n }\n return model;\n}\n\n/**\n * 40 statische Plätze erzeugen (Fallback, wenn noch keine DB-Daten)\n */\nfunction createDefaultSeats() {\n const seats = [];\n let number = 1;\n const columns = 8; // 8 x 5 = 40\n const rows = 5;\n for (let row = 0; row < rows; row++) {\n for (let column = 0; column < columns; column++) {\n seats.push({\n id: `P${String(number).padStart(2, \"0\")}`,\n name: `Platz P${String(number).padStart(2, \"0\")}`,\n x: 10 + column * 11.0,\n // X in Prozent\n y: 15 + row * 17.0,\n // Y in Prozent\n status: \"frei\"\n });\n number++;\n }\n }\n return seats;\n}\n\n/**\n * Plätze aus dem Model lesen, ansonsten statische 40 zurückgeben\n */\nfunction getSeats() {\n const model = getModel();\n let seats = model.seats;\n if (typeof seats === \"string\") {\n try {\n seats = JSON.parse(seats);\n } catch (error) {\n seats = [];\n }\n }\n if (seats && Array.isArray(seats.data)) {\n seats = seats.data;\n }\n if (!Array.isArray(seats) || seats.length === 0) {\n // Fallback: 40 statische Plätze\n return createDefaultSeats();\n }\n return seats;\n}\n\n/**\n * Status-Text für Anzeige\n */\nfunction getStatusText(status) {\n const s = String(status || \"\").trim().toLowerCase();\n if (s === \"belegt\" || s === \"gebucht\" || s === \"booked\" || s === \"occupied\") {\n return \"Belegt\";\n }\n return \"Frei\";\n}\nfunction updateSelectedSeatText() {\n const textElement = document.getElementById(\"selectedSeatText\");\n if (!textElement) return;\n if (!selectedSeat) {\n textElement.textContent = \"Kein Platz ausgewählt\";\n return;\n }\n textElement.textContent = `${selectedSeat.name} – Status: ${getStatusText(selectedSeat.status)}`;\n}\n\n/**\n * Platz auswählen (nur wenn frei)\n */\nfunction selectSeat(seat) {\n console.log(\"Sitzplatz geklickt:\", seat);\n if (!seat) {\n console.error(\"Kein Sitzplatzobjekt erhalten.\");\n return;\n }\n const status = String(seat.status ?? seat.Status ?? \"frei\").trim().toLowerCase();\n const isBooked = status === \"belegt\" || status === \"gebucht\" || status === \"booked\" || status === \"occupied\";\n if (isBooked) {\n console.warn(\"Sitzplatz ist nicht frei:\", seat);\n return;\n }\n const spid = String(seat.SPID ?? seat.spid ?? seat.platz_id ?? seat.seatId ?? seat.id ?? \"\").trim();\n if (!spid) {\n console.error(\"Keine SPID im Sitzplatz-Datensatz gefunden:\", seat);\n return;\n }\n const seatName = String(seat.Sitzplatzname ?? seat.sitzplatzname ?? seat.Platzname ?? seat.platz_name ?? seat.name ?? seat.seatName ?? `Platz ${spid}`).trim();\n selectedSeat = {\n ...seat,\n SPID: spid,\n id: spid,\n name: seatName\n };\n console.log(\"Ausgewählter Sitzplatz:\", selectedSeat);\n appsmith.updateModel({\n selectedSeat: {\n SPID: spid,\n id: spid,\n name: seatName,\n status: status\n },\n selectedSeatSPID: spid,\n selectedSeatName: seatName,\n // Kompatibilität mit deinem bisherigen Kalender-Code\n seatId: spid,\n seatName: seatName\n });\n updateSelectedSeatText();\n renderSeats();\n try {\n appsmith.triggerEvent(\"seatSelected\");\n } catch (error) {\n console.warn(\"Event seatSelected ist nicht konfiguriert.\", error);\n }\n}\n\n/**\n * Auswahl löschen\n */\nfunction clearSelection() {\n selectedSeat = null;\n appsmith.updateModel({\n selectedSeat: null,\n seatId: null,\n seatName: null\n });\n updateSelectedSeatText();\n renderSeats();\n}\n\n/**\n * Button (Punkt) für einen Platz erzeugen\n */\nfunction createSeatButton(seat) {\n const button = document.createElement(\"button\");\n const seatId = String(seat.SPID ?? seat.spid ?? seat.platz_id ?? seat.seatId ?? seat.id ?? \"\").trim();\n const seatName = String(seat.Sitzplatzname ?? seat.sitzplatzname ?? seat.Platzname ?? seat.platz_name ?? seat.name ?? seat.seatName ?? `Platz ${seatId}`).trim();\n const status = String(seat.status ?? seat.Status ?? \"frei\").trim().toLowerCase();\n const isBooked = status === \"belegt\" || status === \"gebucht\" || status === \"booked\" || status === \"occupied\";\n button.type = \"button\";\n button.classList.add(\"seat\");\n if (isBooked) {\n button.classList.add(\"belegt\");\n button.disabled = true;\n } else {\n button.classList.add(\"frei\");\n }\n if (selectedSeat && String(selectedSeat.SPID ?? selectedSeat.id) === seatId) {\n button.classList.add(\"selected\");\n }\n button.style.left = `${Number(seat.x) || 0}%`;\n button.style.top = `${Number(seat.y) || 0}%`;\n button.title = `${seatName} – ${getStatusText(status)}`;\n buttLine truncated
},
"theme": "{{appsmith.theme}}",
"topRow": 8,
@@ -67,7 +67,7 @@
"uncompiledSrcDoc": {
"css": "html,\nbody {\n margin: 0;\n width: 100%;\n height: 100%;\n overflow: auto;\n font-family: Arial, sans-serif;\n}\n\n\n/* Bild + Overlay */\n#imgHolder {\n position: relative;\n width: 100%;\n height: 700px; /* feste Höhe, damit sicher nicht 0 */\n overflow: hidden;\n border-radius: 6px;\n background: #f3f4f6;\n}\n\n#floorplanImage {\n position: absolute;\n inset: 0;\n z-index: 1;\n display: block;\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n#seatLayer {\n position: absolute;\n inset: 0;\n z-index: 2;\n width: 100%;\n height: 100%;\n pointer-events: none; /* Punkte bekommen später pointer-events:auto */\n}\n\n/* Sitzpunkte */\n.seat {\n position: absolute;\n z-index: 3;\n display: block;\n width: 18px;\n height: 18px;\n padding: 0;\n transform: translate(-50%, -50%);\n border: 2px solid #ffffff;\n border-radius: 50%;\n background: #22c55e;\n color: #22c55e;\n cursor: pointer;\n pointer-events: auto;\n appearance: none;\n box-sizing: border-box;\n box-shadow:\n 0 0 5px currentColor,\n 0 0 10px currentColor;\n transition:\n transform 0.15s ease,\n box-shadow 0.15s ease;\n}\n\n.seat:hover {\n z-index: 10;\n transform: translate(-50%, -50%) scale(1.45);\n box-shadow:\n 0 0 8px currentColor,\n 0 0 16px currentColor;\n}\n\n/* Farben nach Status */\n.seat.frei {\n background: #22c55e; /* Grün */\n color: #22c55e;\n}\n\n.seat.belegt {\n background: #ef4444; /* Rot */\n color: #ef4444;\n}\n\n.seat.selected {\n outline: 3px solid #2563eb;\n outline-offset: 3px;\n}\n\n/* Legende */\n#legend {\n display: flex;\n flex-wrap: wrap;\n gap: 14px;\n margin-top: 8px;\n color: #374151;\n font-size: 13px;\n}\n\n.legendItem {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n}\n\n.legendColor {\n display: inline-block;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n}\n\n.legendColor.frei {\n background: #22c55e;\n}\n\n.legendColor.belegt {\n background: #ef4444;\n}",
"html": "<div id=\"seatPlan\">\n\n <div id=\"imgHolder\">\n <!-- Kein src hier, das Bild kommt über JavaScript -->\n <img id=\"floorplanImage\" alt=\"Büroplan\">\n\t\t<div id=\"seatLayer\"></div>\n\t\t\n\t\t\n </div>\n</div>",
"js": "let selectedSeat = null;\n\n/**\n * Base64-Bild: WICHTIG – Typ muss zum Inhalt passen!\n * Wenn dein String mit \"iVBORw0KGgo...\" beginnt, ist das PNG:\n */\nconst FLOORPLAN_IMAGE =\n \"data:image/jpeg;base64,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 truncated
"js": "let selectedSeat = null;\n\n/**\n * Model aus Appsmith holen\n */\nfunction getModel() {\n const model = appsmith.model || {};\n\n if (typeof model === \"string\") {\n try {\n return JSON.parse(model);\n } catch (error) {\n return {};\n }\n }\n\n return model;\n}\n\n/**\n * 40 statische Plätze erzeugen (Fallback, wenn noch keine DB-Daten)\n */\nfunction createDefaultSeats() {\n const seats = [];\n let number = 1;\n\n const columns = 8; // 8 x 5 = 40\n const rows = 5;\n\n for (let row = 0; row < rows; row++) {\n for (let column = 0; column < columns; column++) {\n seats.push({\n id: `P${String(number).padStart(2, \"0\")}`,\n name: `Platz P${String(number).padStart(2, \"0\")}`,\n x: 10 + column * 11.0, // X in Prozent\n y: 15 + row * 17.0, // Y in Prozent\n status: \"frei\"\n });\n number++;\n }\n }\n\n return seats;\n}\n\n/**\n * Plätze aus dem Model lesen, ansonsten statische 40 zurückgeben\n */\nfunction getSeats() {\n const model = getModel();\n\n let seats = model.seats;\n\n if (typeof seats === \"string\") {\n try {\n seats = JSON.parse(seats);\n } catch (error) {\n seats = [];\n }\n }\n\n if (seats && Array.isArray(seats.data)) {\n seats = seats.data;\n }\n\n if (!Array.isArray(seats) || seats.length === 0) {\n // Fallback: 40 statische Plätze\n return createDefaultSeats();\n }\n\n return seats;\n}\n\n/**\n * Status-Text für Anzeige\n */\nfunction getStatusText(status) {\n const s = String(status || \"\").trim().toLowerCase();\n\n if (s === \"belegt\" || s === \"gebucht\" || s === \"booked\" || s === \"occupied\") {\n return \"Belegt\";\n }\n\n return \"Frei\";\n}\n\nfunction updateSelectedSeatText() {\n const textElement = document.getElementById(\"selectedSeatText\");\n if (!textElement) return;\n\n if (!selectedSeat) {\n textElement.textContent = \"Kein Platz ausgewählt\";\n return;\n }\n\n textElement.textContent =\n `${selectedSeat.name} – Status: ${getStatusText(selectedSeat.status)}`;\n}\n\n/**\n * Platz auswählen (nur wenn frei)\n */\nfunction selectSeat(seat) {\n console.log(\"Sitzplatz geklickt:\", seat);\n\n if (!seat) {\n console.error(\"Kein Sitzplatzobjekt erhalten.\");\n return;\n }\n\n const status = String(\n seat.status ??\n seat.Status ??\n \"frei\"\n ).trim().toLowerCase();\n\n const isBooked =\n status === \"belegt\" ||\n status === \"gebucht\" ||\n status === \"booked\" ||\n status === \"occupied\";\n\n if (isBooked) {\n console.warn(\"Sitzplatz ist nicht frei:\", seat);\n return;\n }\n\n const spid = String(\n seat.SPID ??\n seat.spid ??\n seat.platz_id ??\n seat.seatId ??\n seat.id ??\n \"\"\n ).trim();\n\n if (!spid) {\n console.error(\n \"Keine SPID im Sitzplatz-Datensatz gefunden:\",\n seat\n );\n return;\n }\n\n const seatName = String(\n seat.Sitzplatzname ??\n seat.sitzplatzname ??\n seat.Platzname ??\n seat.platz_name ??\n seat.name ??\n seat.seatName ??\n `Platz ${spid}`\n ).trim();\n\n selectedSeat = {\n ...seat,\n SPID: spid,\n id: spid,\n name: seatName\n };\n\n console.log(\n \"Ausgewählter Sitzplatz:\",\n selectedSeat\n );\n\n appsmith.updateModel({\n selectedSeat: {\n SPID: spid,\n id: spid,\n name: seatName,\n status: status\n },\n\n selectedSeatSPID: spid,\n selectedSeatName: seatName,\n\n // Kompatibilität mit deinem bisherigen Kalender-Code\n seatId: spid,\n seatName: seatName\n });\n\n updateSelectedSeatText();\n renderSeats();\n\n try {\n appsmith.triggerEvent(\"seatSelected\");\n } catch (error) {\n console.warn(\n \"Event seatSelected ist nicht konfiguriert.\",\n error\n );\n }\n}\n\n/**\n * Auswahl löschen\n */\nfunction clearSelection() {\n selectedSeat = null;\n\n appsmith.updateModel({\n selectedSeat: null,\n seatId: null,\n seatName: null\n });\n\n updateSelectedSeatText();\n renderSeats();\n}\n\n/**\n * Button (Punkt) für einen Platz erzeugen\n */\nfunction createSeatButton(seat) {\n const button =\n document.createElement(\n \"button\"\n );\n\n const seatId =\n String(\n seat.SPID ??\n seat.spid ??\n seat.platz_id ??\n seat.seatId ??\n seat.id ??\n \"\"\n ).trim();\n\n const seatName =\n String(\n seat.Sitzplatzname ??\n seat.sitzplatzname ??\n seat.Platzname ??\n seat.platz_name ??\n seat.name ??\n seat.seatName ??\n `Platz ${seatId}`\n ).trim();\n\n const status =\n String(\n seat.status ??\n seat.Status ??\n \"frei\"\n )\n .trim()\n .toLowerCase();\n\n const isBooked =\n status === \"belegt\" ||\n status === \"gebucht\" ||\n status === \"booked\" ||\n status === \"occupied\";\n\n button.type = \"button\";\n\n button.classList.add(\n \Line truncated
},
"version": 1,
"widgetId": "londvdt302",