Files
CPLv4.0/components/main/einausgaenge/modals/DigitalOutputsModal.tsx
ISA d4ba8f5b2e feat: Integration von CGI-Platzhaltern für digitale Ausgänge (DASx, DANx)
- Platzhalter in da.js auf neue Struktur (DAS1–DAS4, DAN1–DAN4) umgestellt
- fetchDigitalOutputsService liest Werte dynamisch über da.js vom CPL-Webserver
- Schreibvorgänge via window.location.href mit CGI-Parametern (DASx=, DANx=)
- Umschaltlogik zwischen Entwicklungs- und Produktionsmodus eingebaut
- Modal-Speichern aktualisiert sowohl Status als auch Bezeichnung per CGI
- Unterstützung für lokale Mockdaten über API bleibt bestehen
2025-05-09 10:59:18 +02:00

137 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"; // /components/main/einausgaenge/modals/DigitalOutputsModal.tsx
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../../../redux/store";
export default function DigitalOutputsModal({
selectedOutput,
closeOutputModal,
isOpen,
}: {
selectedOutput: any;
closeOutputModal: () => void;
isOpen: boolean;
}) {
const allOutputs = useSelector(
(state: RootState) => state.digitalOutputsSlice.outputs
);
const [label, setLabel] = useState("");
const [status, setStatus] = useState(false);
const [timer, setTimer] = useState(0);
const [isSaving, setIsSaving] = useState(false);
const [errorMsg, setErrorMsg] = useState("");
// ✅ Zustand neu setzen, wenn Modal geöffnet oder anderer Ausgang ausgewählt wird
useEffect(() => {
if (isOpen && selectedOutput) {
setLabel(selectedOutput.label || "");
setStatus(selectedOutput.status || false);
setTimer(0);
setErrorMsg("");
}
}, [isOpen, selectedOutput]);
if (!isOpen || !selectedOutput) return null;
const handleSave = async () => {
setIsSaving(true);
setErrorMsg("");
const updatedOutputs = allOutputs.map((output) =>
output.id === selectedOutput.id
? { ...output, label: label.trim(), status }
: output
);
const isCPL = process.env.NEXT_PUBLIC_NODE_ENV === "production";
try {
if (isCPL) {
// ✅ Name speichern (DANx=...)
const nameEncoded = encodeURIComponent(label.trim());
const nameUrl = `/CPL?dummy.htm&DAN${selectedOutput.id}=${nameEncoded}`;
// ✅ Status speichern (DASx=...)
const statusUrl = `/CPL?dummy.htm&DAS${selectedOutput.id}=${
status ? 1 : 0
}`;
// 🟢 Beide nacheinander senden (wichtig bei älteren CPL-Versionen)
window.location.href = nameUrl; // Name zuerst (ggf. durch Refresh überschrieben)
setTimeout(() => {
window.location.href = statusUrl;
}, 300); // kleine Verzögerung (optional)
// 💡 Modal wird nicht automatisch geschlossen — da Seite neu lädt.
} else {
// 🧪 Lokaler Entwicklungsmodus
const res = await fetch("/api/cpl/updateDigitalOutputs", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ outputs: updatedOutputs }),
});
if (!res.ok) {
const err = await res.json();
setErrorMsg(err?.error || "Fehler beim Speichern.");
} else {
console.log(
"✅ Status & Label gespeichert für Ausgang",
selectedOutput.id
);
closeOutputModal();
}
}
} catch (err) {
setErrorMsg("❌ Fehler beim Speichern.");
} finally {
setIsSaving(false);
}
};
return (
<div className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 flex justify-center items-center z-50">
<div className="bg-white rounded-lg shadow-lg p-6 w-1/2 max-w-lg">
<div className="mb-4 border-b pb-2 flex justify-between items-center">
<h2 className="text-base font-bold">
Einstellungen Schaltausgang {selectedOutput.id}
</h2>
<button
onClick={closeOutputModal}
className="text-2xl hover:text-gray-400"
aria-label="Modal schließen"
>
<i className="bi bi-x-circle-fill"></i>
</button>
</div>
<div className="grid grid-cols-2 gap-x-4 gap-y-3">
<div>
<span className="font-normal">Bezeichnung:</span>
</div>
<input
type="text"
value={label}
onChange={(e) => setLabel(e.target.value)}
className="w-full border border-gray-300 rounded px-3 py-2"
placeholder="z.B. Licht Relais 1"
/>
</div>
{errorMsg && <p className="text-red-600 text-sm mb-2">{errorMsg}</p>}
<div className="flex justify-end gap-2 mt-6">
<button
onClick={handleSave}
disabled={isSaving}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
{isSaving ? "Speichern..." : "Speichern"}
</button>
</div>
</div>
</div>
);
}