docs: Zusatzfunktionen (Kai, 25.06.2025) in TODO.md ergänzt
This commit is contained in:
@@ -6,5 +6,5 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
|
|||||||
NEXT_PUBLIC_EXPORT_STATIC=false
|
NEXT_PUBLIC_EXPORT_STATIC=false
|
||||||
NEXT_PUBLIC_USE_CGI=false
|
NEXT_PUBLIC_USE_CGI=false
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.444
|
NEXT_PUBLIC_APP_VERSION=1.6.445
|
||||||
NEXT_PUBLIC_CPL_MODE=jsSimulatedProd # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
NEXT_PUBLIC_CPL_MODE=jsSimulatedProd # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
||||||
@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
|
|||||||
NEXT_PUBLIC_EXPORT_STATIC=true
|
NEXT_PUBLIC_EXPORT_STATIC=true
|
||||||
NEXT_PUBLIC_USE_CGI=true
|
NEXT_PUBLIC_USE_CGI=true
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.444
|
NEXT_PUBLIC_APP_VERSION=1.6.445
|
||||||
NEXT_PUBLIC_CPL_MODE=production
|
NEXT_PUBLIC_CPL_MODE=production
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
## [1.6.445] – 2025-06-25
|
||||||
|
|
||||||
|
- docs: README
|
||||||
|
|
||||||
|
---
|
||||||
## [1.6.444] – 2025-06-23
|
## [1.6.444] – 2025-06-23
|
||||||
|
|
||||||
- feat: digitalOutputs separate jsSimulatedProd mode
|
- feat: digitalOutputs separate jsSimulatedProd mode
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ import handleAdminLogin from "./handlers/handleAdminLogin";
|
|||||||
|
|
||||||
ReactModal.setAppElement("#__next");
|
ReactModal.setAppElement("#__next");
|
||||||
|
|
||||||
function SettingModal({ showModal, onClose }) {
|
function SettingModal({
|
||||||
|
showModal,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
showModal: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}) {
|
||||||
const { isAdminLoggedIn, logoutAdmin } = useAdminAuth(showModal);
|
const { isAdminLoggedIn, logoutAdmin } = useAdminAuth(showModal);
|
||||||
|
|
||||||
const { formValues, setFormValues } = useSystemSettings(showModal);
|
const { formValues, setFormValues } = useSystemSettings(showModal);
|
||||||
@@ -72,9 +78,21 @@ function SettingModal({ showModal, onClose }) {
|
|||||||
const [ntp2, setNtp2] = useState(ntp2_Redux || "");
|
const [ntp2, setNtp2] = useState(ntp2_Redux || "");
|
||||||
const [ntp3, setNtp3] = useState(ntp3_Redux || "");
|
const [ntp3, setNtp3] = useState(ntp3_Redux || "");
|
||||||
const [ntpTimezone, setNtpTimezone] = useState(ntpTimezone_Redux || "");
|
const [ntpTimezone, setNtpTimezone] = useState(ntpTimezone_Redux || "");
|
||||||
const [active, setActive] = useState(active_Redux || "");
|
const [active, setActive] = useState<boolean>(
|
||||||
|
typeof active_Redux === "boolean" ? active_Redux : active_Redux === "true"
|
||||||
|
);
|
||||||
|
|
||||||
const [originalValues, setOriginalValues] = useState({});
|
const [originalValues, setOriginalValues] = useState({
|
||||||
|
name: name,
|
||||||
|
ip: ip,
|
||||||
|
subnet: subnet,
|
||||||
|
gateway: gateway,
|
||||||
|
ntp1: ntp1,
|
||||||
|
ntp2: ntp2,
|
||||||
|
ntp3: ntp3,
|
||||||
|
ntpTimezone: ntpTimezone,
|
||||||
|
active: active,
|
||||||
|
});
|
||||||
const currentValues = {
|
const currentValues = {
|
||||||
name,
|
name,
|
||||||
ip,
|
ip,
|
||||||
@@ -228,12 +246,14 @@ function SettingModal({ showModal, onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block font-medium">NTP Active:</label>
|
<label className="block font-medium">NTP Active:</label>
|
||||||
<input
|
<select
|
||||||
type="text"
|
|
||||||
className="border border-gray-300 rounded p-1 xl:p-2 w-full"
|
className="border border-gray-300 rounded p-1 xl:p-2 w-full"
|
||||||
value={active}
|
value={active ? "true" : "false"}
|
||||||
onChange={(e) => setActive(e.target.value)}
|
onChange={(e) => setActive(e.target.value === "true")}
|
||||||
/>
|
>
|
||||||
|
<option value="true">true</option>
|
||||||
|
<option value="false">false</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -241,13 +261,13 @@ function SettingModal({ showModal, onClose }) {
|
|||||||
<div className="flex flex-col md:flex-row justify-between mt-4 gap-2">
|
<div className="flex flex-col md:flex-row justify-between mt-4 gap-2">
|
||||||
<button
|
<button
|
||||||
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
||||||
onClick={handleReboot}
|
onClick={() => handleReboot()}
|
||||||
>
|
>
|
||||||
Neustart CPL
|
Neustart CPL
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
isAdminLoggedIn ? handleAdminLogout() : setShowLoginForm(true)
|
isAdminLoggedIn ? logoutAdmin() : setShowLoginForm(true)
|
||||||
}
|
}
|
||||||
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../../redux/store";
|
import { RootState } from "../../../../redux/store";
|
||||||
export function useSystemSettings(showModal: boolean) {
|
export function useSystemSettings(showModal: boolean) {
|
||||||
const settings = useSelector((state: RootState) => state.systemSettings);
|
const settings = useSelector((state: RootState) => state.systemSettingsSlice);
|
||||||
const [formValues, setFormValues] = useState(settings);
|
const [formValues, setFormValues] = useState(settings);
|
||||||
const [originalValues, setOriginalValues] = useState({});
|
const [originalValues, setOriginalValues] = useState({});
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,24 @@ export function generateKeyAndIV() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generiert einen verschlüsselten Token
|
// Generiert einen verschlüsselten Token
|
||||||
export function generateToken(user) {
|
interface User {
|
||||||
const payload = {
|
username: string;
|
||||||
|
role: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TokenPayload {
|
||||||
|
username: string;
|
||||||
|
role: string;
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateToken(user: User): string {
|
||||||
|
const payload: TokenPayload = {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
||||||
};
|
};
|
||||||
const token = JSON.stringify(payload);
|
const token: string = JSON.stringify(payload);
|
||||||
const { key, iv } = generateKeyAndIV();
|
const { key, iv } = generateKeyAndIV();
|
||||||
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,11 @@ export default function AnalogInputsChart({
|
|||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const { data, isLoading, error } = useSelector(
|
const { data, isLoading, error } = useSelector(
|
||||||
(state: RootState) => state.analogInputsHistory
|
(state: RootState) => state.analogInputsHistory
|
||||||
);
|
) as {
|
||||||
|
data: { [key: string]: any[] };
|
||||||
|
isLoading: boolean;
|
||||||
|
error: any;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getAnalogInputsHistoryThunk());
|
dispatch(getAnalogInputsHistoryThunk());
|
||||||
@@ -95,7 +99,7 @@ export default function AnalogInputsChart({
|
|||||||
x: {
|
x: {
|
||||||
type: "time" as const,
|
type: "time" as const,
|
||||||
time: {
|
time: {
|
||||||
unit: "hour",
|
unit: "hour" as const,
|
||||||
tooltipFormat: "HH:mm 'Uhr' dd.MM.",
|
tooltipFormat: "HH:mm 'Uhr' dd.MM.",
|
||||||
displayFormats: {
|
displayFormats: {
|
||||||
hour: "HH:mm",
|
hour: "HH:mm",
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
"use client"; // /components/main/analogInputs/AnalogInputsTable.tsx
|
"use client"; // /components/main/analogInputs/AnalogInputsTable.tsx
|
||||||
|
|
||||||
|
export type AnalogInput = {
|
||||||
|
id: number;
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
unit?: string; // Make unit optional if it may not exist
|
||||||
|
offset: number;
|
||||||
|
factor: number;
|
||||||
|
loggerInterval: number;
|
||||||
|
weighting: number;
|
||||||
|
};
|
||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { RootState, AppDispatch } from "@/redux/store";
|
import { RootState, AppDispatch } from "@/redux/store";
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import outputIcon from "@iconify/icons-mdi/output";
|
|||||||
import switchIcon from "@iconify/icons-ion/switch";
|
import switchIcon from "@iconify/icons-ion/switch";
|
||||||
import { setDigitalOutputs } from "@/redux/slices/digitalOutputsSlice";
|
import { setDigitalOutputs } from "@/redux/slices/digitalOutputsSlice";
|
||||||
|
|
||||||
export default function DigitalOutputsWidget({ openOutputModal }) {
|
interface DigitalOutputsWidgetProps {
|
||||||
|
openOutputModal: (output: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DigitalOutputsWidget({
|
||||||
|
openOutputModal,
|
||||||
|
}: DigitalOutputsWidgetProps) {
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const digitalOutputs = useSelector(
|
const digitalOutputs = useSelector(
|
||||||
(state: RootState) => state.digitalOutputsSlice.outputs
|
(state: RootState) => state.digitalOutputsSlice.outputs
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../../redux/store";
|
import { RootState } from "../../../../redux/store";
|
||||||
export function useSystemSettings(showModal: boolean) {
|
export function useSystemSettings(showModal: boolean) {
|
||||||
const settings = useSelector((state: RootState) => state.systemSettings);
|
const settings = useSelector((state: RootState) => state.systemSettingsSlice);
|
||||||
const [formValues, setFormValues] = useState(settings);
|
const [formValues, setFormValues] = useState(settings);
|
||||||
const [originalValues, setOriginalValues] = useState({});
|
const [originalValues, setOriginalValues] = useState({});
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,22 @@ export function generateKeyAndIV() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generiert einen verschlüsselten Token
|
// Generiert einen verschlüsselten Token
|
||||||
export function generateToken(user) {
|
interface UserPayload {
|
||||||
const payload = {
|
username: string;
|
||||||
|
role: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TokenPayload extends UserPayload {
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateToken(user: UserPayload): string {
|
||||||
|
const payload: TokenPayload = {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
||||||
};
|
};
|
||||||
const token = JSON.stringify(payload);
|
const token: string = JSON.stringify(payload);
|
||||||
const { key, iv } = generateKeyAndIV();
|
const { key, iv } = generateKeyAndIV();
|
||||||
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,22 @@ import { Icon } from "@iconify/react";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../redux/store";
|
import { RootState } from "../../../redux/store";
|
||||||
|
|
||||||
const VersionInfo: React.FC = () => {
|
type VersionInfoProps = {
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
||||||
const appVersion =
|
const appVersion =
|
||||||
useSelector((state: RootState) => state.systemSettingsSlice.appVersion) ||
|
useSelector((state: RootState) => state.systemSettingsSlice.appVersion) ||
|
||||||
"Unbekannt";
|
"Unbekannt";
|
||||||
const webVersion = useSelector(
|
const webVersion = useSelector(
|
||||||
(state: RootState) => state.webVersionSlice.appVersion
|
(state: RootState) => state.webVersionSlice.appVersion
|
||||||
); // Webversion aus Redux holen
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200 w-full laptop:p-2">
|
<div
|
||||||
|
className={`bg-gray-50 rounded-lg shadow-sm border border-gray-200 w-full laptop:p-2 ${className}`}
|
||||||
|
>
|
||||||
<h2 className="text-lg font-semibold text-gray-700 mb-2">
|
<h2 className="text-lg font-semibold text-gray-700 mb-2">
|
||||||
Versionsinformationen
|
Versionsinformationen
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
11
docs/TODO.md
11
docs/TODO.md
@@ -1,3 +1,12 @@
|
|||||||
## ✅ TODOs
|
## ✅ TODOs
|
||||||
|
|
||||||
- [ ] TODO: In digitale Eingänge Modal Out of Service geht nicht in json , jsSimulatedProd und production. readOnly?
|
## 🔄 Zusatzfunktionen (prio niedrig) – Kai, 25. Juni 2025
|
||||||
|
|
||||||
|
- [ ] Messwertanzeige eines Eingangs: Zeitraum einstellbar, Bezeichnung und Einheit in die Messkurve, Datum in die Messkurve
|
||||||
|
- [ ] Startseite Meldungen: Spalte Quelle wie bei Berichte (nicht "Modul")
|
||||||
|
- [ ] Startseite Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
|
||||||
|
- [ ] Berichte Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
|
||||||
|
- [ ] Filter für Quelle
|
||||||
|
- [ ] System: Button bei jedem Messwert für Detailansicht (inkl. Kurve im Popup und Zeitauswahl)
|
||||||
|
- [ ] Einstellungen – Benutzerverwaltung: Admin kann Adminpasswort ändern
|
||||||
|
- [ ] Einstellungen – OPC: Anzahl der aktuellen Clients (ggf. KAS-Variable einbauen)
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ var win_analogInputsValues = [
|
|||||||
4.771072, 5.665244, 0.005467, -0.007468, 0.000002, 0.000001, 0.000001,
|
4.771072, 5.665244, 0.005467, -0.007468, 0.000002, 0.000001, 0.000001,
|
||||||
0.070007,
|
0.070007,
|
||||||
];
|
];
|
||||||
var win_analogInputsLabels = ["AE 1", "AE 2", "AE 3", "AE 4", "AE 5", "AE 6", "AE 7", "AE 8", ];
|
var win_analogInputsLabels = ["AE 11", "AE 2", "AE 3", "AE 4", "AE 5", "AE 6", "AE 7", "AE 8", ];
|
||||||
var win_analogInputsOffset = [10.995, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0];
|
var win_analogInputsOffset = [21.999, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0];
|
||||||
var win_analogInputsFactor = [11.996, 1.0, 1.0, 1, 1.0, 1.0, 1.0, 1.0];
|
var win_analogInputsFactor = [21.999, 1.0, 1.0, 1, 1.0, 1.0, 1.0, 1.0];
|
||||||
var win_analogInputsLoggerIntervall = [6, 10, 10, 10, 10, 10, 10, 10];
|
var win_analogInputsLoggerIntervall = [21, 10, 10, 10, 10, 10, 10, 10];
|
||||||
var win_analogInputsUnits = ["mA", "V", "V", "V", "mA", "mA", "mA", "mA"];
|
var win_analogInputsUnits = ["V", "V", "V", "V", "mA", "mA", "mA", "mA"];
|
||||||
var win_analogInputsWeighting = [0, 0, 0, 0, 0, 0, 0, 0];
|
var win_analogInputsWeighting = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
63
mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json
Normal file
63
mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"win_analogInputsValues": [
|
||||||
|
"126.812080",
|
||||||
|
"5.680176",
|
||||||
|
"-0.015003",
|
||||||
|
"0.009538",
|
||||||
|
"-0.000002",
|
||||||
|
"0.000003",
|
||||||
|
"-0.000005",
|
||||||
|
"0.000000"
|
||||||
|
],
|
||||||
|
"win_analogInputsLabels": [
|
||||||
|
"'AE 1'",
|
||||||
|
"'AE 2'",
|
||||||
|
"'AE 3'",
|
||||||
|
"'AE 4'",
|
||||||
|
"'AE 5'",
|
||||||
|
"'AE 6'",
|
||||||
|
"'AE 7'",
|
||||||
|
"'AE 8'"
|
||||||
|
],
|
||||||
|
"win_analogInputsUnits": [
|
||||||
|
"'V'",
|
||||||
|
"'V'",
|
||||||
|
"'V'",
|
||||||
|
"'V'",
|
||||||
|
"'mA'",
|
||||||
|
"'mA'",
|
||||||
|
"'mA'",
|
||||||
|
"'mA'"
|
||||||
|
],
|
||||||
|
"win_analogInputsFactor": [
|
||||||
|
"21.999",
|
||||||
|
"1.000",
|
||||||
|
"1.000",
|
||||||
|
"1.000",
|
||||||
|
"1.000",
|
||||||
|
"1.000",
|
||||||
|
"1.000",
|
||||||
|
"1.000"
|
||||||
|
],
|
||||||
|
"win_analogInputsOffset": [
|
||||||
|
"21.999",
|
||||||
|
"0.000",
|
||||||
|
"0.000",
|
||||||
|
"0.000",
|
||||||
|
"0.000",
|
||||||
|
"0.000",
|
||||||
|
"0.000",
|
||||||
|
"0.000"
|
||||||
|
],
|
||||||
|
"win_analogInputsWeighting": ["0", "0", "0", "0", "0", "0", "0", "0"],
|
||||||
|
"win_analogInputsLoggerIntervall": [
|
||||||
|
"21",
|
||||||
|
"10",
|
||||||
|
"10",
|
||||||
|
"10",
|
||||||
|
"10",
|
||||||
|
"10",
|
||||||
|
"10",
|
||||||
|
"10"
|
||||||
|
]
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.444",
|
"version": "1.6.445",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.444",
|
"version": "1.6.445",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^5.1.0",
|
"@fontsource/roboto": "^5.1.0",
|
||||||
"@iconify-icons/ri": "^1.2.10",
|
"@iconify-icons/ri": "^1.2.10",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.444",
|
"version": "1.6.445",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ function AppContent({
|
|||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [sessionExpired, setSessionExpired] = useState(false);
|
const [sessionExpired, setSessionExpired] = useState(false);
|
||||||
|
const mode = "DIA0"; // oder aus Router oder Session
|
||||||
|
const type = 0; // Beispiel: 0 für "loop", 1 für "iso" (bitte ggf. anpassen)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let intervalId: NodeJS.Timeout;
|
let intervalId: NodeJS.Timeout;
|
||||||
const pathname = window.location.pathname;
|
const pathname = window.location.pathname;
|
||||||
@@ -71,11 +72,19 @@ function AppContent({
|
|||||||
} else if (pathname.includes("tdrRef")) {
|
} else if (pathname.includes("tdrRef")) {
|
||||||
dispatch(getAllTDRReferenceChartThunk());
|
dispatch(getAllTDRReferenceChartThunk());
|
||||||
} else if (pathname.includes("tdrSlot")) {
|
} else if (pathname.includes("tdrSlot")) {
|
||||||
dispatch(getReferenceCurveBySlotThunk());
|
dispatch(getReferenceCurveBySlotThunk(1));
|
||||||
} else if (pathname.includes("tdrId")) {
|
} else if (pathname.includes("tdrId")) {
|
||||||
dispatch(getTDRChartDataByIdThunk());
|
dispatch(getTDRChartDataByIdThunk(1));
|
||||||
} else if (pathname.includes("loopChart")) {
|
} else if (pathname.includes("loopChart")) {
|
||||||
dispatch(getLoopChartDataThunk());
|
dispatch(
|
||||||
|
getLoopChartDataThunk({
|
||||||
|
mode,
|
||||||
|
type,
|
||||||
|
slotNumber: 1, // Beispielwert, ggf. anpassen
|
||||||
|
vonDatum: new Date().toISOString().split("T")[0], // Beispiel: heutiges Datum
|
||||||
|
bisDatum: new Date().toISOString().split("T")[0], // Beispiel: heutiges Datum
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,13 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode === "jsSimulatedProd") {
|
if (mode === "jsSimulatedProd") {
|
||||||
const analogInputsScript = fs.readFileSync(
|
const filePath = path.join(
|
||||||
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.js",
|
process.cwd(),
|
||||||
"utf-8"
|
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json"
|
||||||
);
|
);
|
||||||
res.setHeader("Content-Type", "application/javascript");
|
const jsonContent = fs.readFileSync(filePath, "utf-8");
|
||||||
res.status(200).send(analogInputsScript);
|
const data = JSON.parse(jsonContent);
|
||||||
|
res.status(200).json(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default function handler(req, res) {
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { slot } = req.query;
|
const { slot } = req.query;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
res
|
res
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// pages/api/cpl/tdrMessungStartenMockHandler.ts
|
// pages/api/cpl/tdrMessungStartenMockHandler.ts
|
||||||
export default function handler(req, res) {
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { slot } = req.query;
|
const { slot } = req.query;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
res.status(200).json({ message: `TDR simuliert für Slot ${slot}` });
|
res.status(200).json({ message: `TDR simuliert für Slot ${slot}` });
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default function handler(req, res) {
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (req.method !== "POST") {
|
if (req.method !== "POST") {
|
||||||
return res.status(405).json({ message: "Nur POST erlaubt" });
|
return res.status(405).json({ message: "Nur POST erlaubt" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
// /pages/api/cpl/updateKueSettingsDataAPIHandler.ts
|
// /pages/api/cpl/updateKueSettingsDataAPIHandler.ts
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
let { key, value, slot } = req.query;
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
if (
|
||||||
const { key, value, slot } = req.query;
|
typeof key !== "string" ||
|
||||||
|
typeof value !== "string" ||
|
||||||
if (!key || slot === undefined) {
|
typeof slot !== "string"
|
||||||
return res.status(400).json({ error: "Missing key or slot parameter." });
|
) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "Missing or invalid key, value, or slot parameter." });
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockFilePath = path.join(
|
const mockFilePath = path.join(
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
const updates = req.body?.updates;
|
const updates = req.body?.updates;
|
||||||
|
|
||||||
if (!Array.isArray(updates) || updates.length === 0) {
|
if (!Array.isArray(updates) || updates.length === 0) {
|
||||||
@@ -34,7 +38,7 @@ export default async function handler(req, res) {
|
|||||||
.map((v) => (v === "" ? "0" : v))
|
.map((v) => (v === "" ? "0" : v))
|
||||||
.slice(0, 32);
|
.slice(0, 32);
|
||||||
|
|
||||||
values[Number(slot)] = Number(value);
|
values[Number(slot)] = String(Number(value));
|
||||||
|
|
||||||
const newLine = `var ${key} = [\n ${values.join(", ")}\n];`;
|
const newLine = `var ${key} = [\n ${values.join(", ")}\n];`;
|
||||||
fileContent = fileContent.replace(regex, newLine);
|
fileContent = fileContent.replace(regex, newLine);
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import { getKueDataThunk } from "../redux/thunks/getKueDataThunk";
|
|||||||
function Kabelueberwachung() {
|
function Kabelueberwachung() {
|
||||||
const dispatch: AppDispatch = useDispatch();
|
const dispatch: AppDispatch = useDispatch();
|
||||||
const searchParams = useSearchParams(); // URL-Parameter holen
|
const searchParams = useSearchParams(); // URL-Parameter holen
|
||||||
const initialRack = parseInt(searchParams.get("rack")) || 1; // Rack-Nummer aus URL oder 1
|
const initialRack = parseInt(searchParams.get("rack") ?? "1") || 1; // Rack-Nummer aus URL oder 1
|
||||||
|
|
||||||
const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert
|
const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert
|
||||||
const [alarmStatus, setAlarmStatus] = useState([]); // Alarmstatus
|
const [alarmStatus, setAlarmStatus] = useState<boolean[]>([]); // Alarmstatus
|
||||||
|
|
||||||
// Redux-Variablen aus dem Store abrufen
|
// Redux-Variablen aus dem Store abrufen
|
||||||
const {
|
const {
|
||||||
@@ -25,21 +25,23 @@ function Kabelueberwachung() {
|
|||||||
kueResidence,
|
kueResidence,
|
||||||
kueCableBreak,
|
kueCableBreak,
|
||||||
kueGroundFault,
|
kueGroundFault,
|
||||||
} = useSelector((state) => state.kueDataSlice);
|
} = useSelector((state: RootState) => state.kueDataSlice);
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
// 🚀 **TDR-Daten bereits in Redux abrufen**
|
// 🚀 **TDR-Daten bereits in Redux abrufen**
|
||||||
// Redux-Variablen abrufen
|
// Redux-Variablen abrufen
|
||||||
const tdrData = useSelector((state) => state.tdrChartSlice.data);
|
const tdrData = useSelector((state: RootState) => state.tdrChartSlice.data);
|
||||||
const loading = useSelector((state) => state.tdrChartSlice.loading);
|
const loading = useSelector(
|
||||||
const error = useSelector((state) => state.tdrChartSlice.error);
|
(state: RootState) => state.tdrChartSlice.loading
|
||||||
|
);
|
||||||
|
const error = useSelector((state: RootState) => state.tdrChartSlice.error);
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
// Alarmstatus basierend auf Redux-Variablen berechnen
|
// Alarmstatus basierend auf Redux-Variablen berechnen
|
||||||
const updateAlarmStatus = () => {
|
const updateAlarmStatus = () => {
|
||||||
const updatedAlarmStatus = kueIso.map((_, index) => {
|
const updatedAlarmStatus = kueIso.map((_, index) => {
|
||||||
return (
|
return !!(
|
||||||
(kueAlarm1 && kueAlarm1[index]) ||
|
(kueAlarm1 && kueAlarm1[index]) ||
|
||||||
(kueAlarm2 && kueAlarm2[index]) ||
|
(kueAlarm2 && kueAlarm2[index]) ||
|
||||||
(kueCableBreak && kueCableBreak[index]) ||
|
(kueCableBreak && kueCableBreak[index]) ||
|
||||||
@@ -66,7 +68,8 @@ function Kabelueberwachung() {
|
|||||||
}));
|
}));
|
||||||
//console.log("Alle Module:", allModules);
|
//console.log("Alle Module:", allModules);
|
||||||
|
|
||||||
const racks = {
|
type RackKey = "rack1" | "rack2" | "rack3" | "rack4";
|
||||||
|
const racks: Record<RackKey, typeof allModules> = {
|
||||||
rack1: allModules.slice(0, 8),
|
rack1: allModules.slice(0, 8),
|
||||||
rack2: allModules.slice(8, 16),
|
rack2: allModules.slice(8, 16),
|
||||||
rack3: allModules.slice(16, 24),
|
rack3: allModules.slice(16, 24),
|
||||||
@@ -92,7 +95,11 @@ function Kabelueberwachung() {
|
|||||||
); */
|
); */
|
||||||
|
|
||||||
// Funktion zum Wechseln des Racks
|
// Funktion zum Wechseln des Racks
|
||||||
const changeRack = (rack) => {
|
interface ChangeRackFn {
|
||||||
|
(rack: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeRack: ChangeRackFn = (rack) => {
|
||||||
setActiveRack(rack);
|
setActiveRack(rack);
|
||||||
console.log(`Aktives Rack geändert zu: ${rack}`);
|
console.log(`Aktives Rack geändert zu: ${rack}`);
|
||||||
};
|
};
|
||||||
@@ -144,8 +151,7 @@ function Kabelueberwachung() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row space-x-8 xl:space-x-0 2xl:space-x-8 qhd:space-x-16 ml-[5%] mt-[5%]">
|
{racks[`rack${activeRack}` as RackKey].map((slot, index) => {
|
||||||
{racks[`rack${activeRack}`].map((slot, index) => {
|
|
||||||
const slotIndex = index + (activeRack - 1) * 8;
|
const slotIndex = index + (activeRack - 1) * 8;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex">
|
<div key={index} className="flex">
|
||||||
@@ -156,12 +162,12 @@ function Kabelueberwachung() {
|
|||||||
kueOnline={slot.kueOnlineStatus}
|
kueOnline={slot.kueOnlineStatus}
|
||||||
alarmStatus={slot.alarmStatus}
|
alarmStatus={slot.alarmStatus}
|
||||||
slotIndex={slotIndex}
|
slotIndex={slotIndex}
|
||||||
|
tdrLocation={[]} // TODO: Replace with actual tdrLocation data if available
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
72
public/CPL/SERVICE/analogInputs.json
Normal file
72
public/CPL/SERVICE/analogInputs.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"win_analogInputsValues": [
|
||||||
|
"<%=AAV01%>",
|
||||||
|
"<%=AAV02%>",
|
||||||
|
"<%=AAV03%>",
|
||||||
|
"<%=AAV04%>",
|
||||||
|
"<%=AAV05%>",
|
||||||
|
"<%=AAV06%>",
|
||||||
|
"<%=AAV07%>",
|
||||||
|
"<%=AAV08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsLabels": [
|
||||||
|
"<%=ACN01%>",
|
||||||
|
"<%=ACN02%>",
|
||||||
|
"<%=ACN03%>",
|
||||||
|
"<%=ACN04%>",
|
||||||
|
"<%=ACN05%>",
|
||||||
|
"<%=ACN06%>",
|
||||||
|
"<%=ACN07%>",
|
||||||
|
"<%=ACN08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsUnits": [
|
||||||
|
"<%=ACU01%>",
|
||||||
|
"<%=ACU02%>",
|
||||||
|
"<%=ACU03%>",
|
||||||
|
"<%=ACU04%>",
|
||||||
|
"<%=ACU05%>",
|
||||||
|
"<%=ACU06%>",
|
||||||
|
"<%=ACU07%>",
|
||||||
|
"<%=ACU08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsFactor": [
|
||||||
|
"<%=ACF01%>",
|
||||||
|
"<%=ACF02%>",
|
||||||
|
"<%=ACF03%>",
|
||||||
|
"<%=ACF04%>",
|
||||||
|
"<%=ACF05%>",
|
||||||
|
"<%=ACF06%>",
|
||||||
|
"<%=ACF07%>",
|
||||||
|
"<%=ACF08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsOffset": [
|
||||||
|
"<%=ACO01%>",
|
||||||
|
"<%=ACO02%>",
|
||||||
|
"<%=ACO03%>",
|
||||||
|
"<%=ACO04%>",
|
||||||
|
"<%=ACO05%>",
|
||||||
|
"<%=ACO06%>",
|
||||||
|
"<%=ACO07%>",
|
||||||
|
"<%=ACO08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsWeighting": [
|
||||||
|
"<%=ACS01%>",
|
||||||
|
"<%=ACS02%>",
|
||||||
|
"<%=ACS03%>",
|
||||||
|
"<%=ACS04%>",
|
||||||
|
"<%=ACS05%>",
|
||||||
|
"<%=ACS06%>",
|
||||||
|
"<%=ACS07%>",
|
||||||
|
"<%=ACS08%>"
|
||||||
|
],
|
||||||
|
"win_analogInputsLoggerIntervall": [
|
||||||
|
"<%=ACL01%>",
|
||||||
|
"<%=ACL02%>",
|
||||||
|
"<%=ACL03%>",
|
||||||
|
"<%=ACL04%>",
|
||||||
|
"<%=ACL05%>",
|
||||||
|
"<%=ACL06%>",
|
||||||
|
"<%=ACL07%>",
|
||||||
|
"<%=ACL08%>"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,61 +2,56 @@
|
|||||||
|
|
||||||
export const fetchAnalogInputsService = async () => {
|
export const fetchAnalogInputsService = async () => {
|
||||||
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
|
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
|
||||||
// ✅ PRODUKTIV: lädt JavaScript vom Gerät über CGI
|
// ✅ PRODUKTIV: lädt JSON-Datei vom Gerät über CGI
|
||||||
if (mode === "production") {
|
if (mode === "production") {
|
||||||
console.log("🔄 Lade analoge Eingänge im Produktionsmodus...");
|
console.log(
|
||||||
const scriptUrl = "/CPL?/CPL/SERVICE/analogInputs.js";
|
"🔄 Lade analoge Eingänge im Produktionsmodus (JSON über CPL?)..."
|
||||||
|
);
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
const res = await fetch("/CPL?/CPL/SERVICE/analogInputs.json", {
|
||||||
const script = document.createElement("script");
|
headers: { Accept: "application/json" },
|
||||||
script.src = scriptUrl;
|
|
||||||
script.async = true;
|
|
||||||
script.onload = () => resolve();
|
|
||||||
script.onerror = () => reject("❌ Fehler beim Laden der analogInputs.js");
|
|
||||||
document.body.appendChild(script);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const win = window as any;
|
if (!res.ok) {
|
||||||
|
throw new Error("❌ Fehler beim Laden der analogInputs.json");
|
||||||
|
}
|
||||||
|
|
||||||
return Array.from({ length: 8 }, (_, i) => ({
|
const data = await res.json();
|
||||||
|
|
||||||
|
return data.win_analogInputsValues.map((value: string, i: number) => ({
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
value: parseFloat(win.win_analogInputsValues[i]),
|
value: parseFloat(value),
|
||||||
label: win.win_analogInputsLabels[i],
|
label: data.win_analogInputsLabels[i].replace(/'/g, ""),
|
||||||
unit: win.win_analogInputsUnits[i],
|
unit: data.win_analogInputsUnits[i].replace(/'/g, ""),
|
||||||
offset: parseFloat(win.win_analogInputsOffset[i]),
|
offset: parseFloat(data.win_analogInputsOffset[i]),
|
||||||
factor: parseFloat(win.win_analogInputsFactor[i]),
|
factor: parseFloat(data.win_analogInputsFactor[i]),
|
||||||
loggerInterval: parseInt(win.win_analogInputsLoggerIntervall[i]),
|
loggerInterval: parseInt(data.win_analogInputsLoggerIntervall[i]),
|
||||||
weighting: parseInt(win.win_analogInputsWeighting[i]),
|
weighting: parseInt(data.win_analogInputsWeighting[i]),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ jsSimulatedProd-MODUS (Script einbinden und aus window lesen)
|
// ✅ jsSimulatedProd-MODUS (Script einbinden und aus window lesen)
|
||||||
else if (mode === "jsSimulatedProd") {
|
else if (mode === "jsSimulatedProd") {
|
||||||
console.log("🔄 Lade simulierte analoge Eingänge im Produktionsmodus...");
|
console.log("🧪 Lade analoge Eingänge im Simulationsmodus (JSON)");
|
||||||
const scriptUrl = "/api/cpl/getAnalogInputsHandler"; // gibt JavaScript zurück
|
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
const res = await fetch("/api/cpl/getAnalogInputsHandler");
|
||||||
const script = document.createElement("script");
|
if (!res.ok)
|
||||||
script.src = scriptUrl;
|
throw new Error("❌ Fehler beim Laden der analogen Eingänge (Mock)");
|
||||||
script.async = true;
|
|
||||||
script.onload = () => resolve();
|
|
||||||
script.onerror = () =>
|
|
||||||
reject("❌ Fehler beim Laden des simulierten Scripts");
|
|
||||||
document.body.appendChild(script);
|
|
||||||
});
|
|
||||||
|
|
||||||
const win = window as any;
|
const data = await res.json();
|
||||||
|
|
||||||
return Array.from({ length: 8 }, (_, i) => ({
|
return data.win_analogInputsValues.map((value: string, i: number) => ({
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
value: parseFloat(win.win_analogInputsValues[i]),
|
value: parseFloat(value),
|
||||||
label: win.win_analogInputsLabels[i],
|
label: data.win_analogInputsLabels[i].replace(/'/g, ""),
|
||||||
unit: win.win_analogInputsUnits[i],
|
unit: data.win_analogInputsUnits[i].replace(/'/g, ""),
|
||||||
offset: parseFloat(win.win_analogInputsOffset[i]),
|
offset: parseFloat(data.win_analogInputsOffset[i]),
|
||||||
factor: parseFloat(win.win_analogInputsFactor[i]),
|
factor: parseFloat(data.win_analogInputsFactor[i]),
|
||||||
loggerInterval: parseInt(win.win_analogInputsLoggerIntervall[i]),
|
loggerInterval: parseInt(data.win_analogInputsLoggerIntervall[i]),
|
||||||
weighting: parseInt(win.win_analogInputsWeighting[i]),
|
weighting: parseInt(data.win_analogInputsWeighting[i]),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ JSON-MODUS (API gibt JSON-Daten zurück)
|
// ✅ JSON-MODUS (API gibt JSON-Daten zurück)
|
||||||
else if (mode === "json") {
|
else if (mode === "json") {
|
||||||
console.log("🔄 Lade analoge Eingänge im JSON-Modus...");
|
console.log("🔄 Lade analoge Eingänge im JSON-Modus...");
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ export async function checkSession(): Promise<boolean> {
|
|||||||
|
|
||||||
if (typeof window === "undefined") return false;
|
if (typeof window === "undefined") return false;
|
||||||
|
|
||||||
return window[expectedGlobalVar] !== undefined;
|
return (
|
||||||
|
(window as unknown as Record<string, unknown>)[expectedGlobalVar] !==
|
||||||
|
undefined
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ export async function loadWindowVariables(): Promise<{
|
|||||||
const win = window as unknown as CustomWindow;
|
const win = window as unknown as CustomWindow;
|
||||||
|
|
||||||
const variablesObj: Record<string, any> = requiredVars.reduce(
|
const variablesObj: Record<string, any> = requiredVars.reduce(
|
||||||
(acc, variable) => {
|
(acc: Record<string, any>, variable: string) => {
|
||||||
if (win[variable] !== undefined) {
|
if (win[variable] !== undefined) {
|
||||||
acc[variable.replace("win_", "")] = win[variable];
|
acc[variable.replace("win_", "")] = win[variable];
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{}
|
{} as Record<string, any>
|
||||||
);
|
);
|
||||||
|
|
||||||
const kueData =
|
const kueData =
|
||||||
|
|||||||
Reference in New Issue
Block a user