Refactor code structure for improved readability and maintainability

This commit is contained in:
jhartworks
2026-09-16 12:41:24 +02:00
parent bfce688f6a
commit 4d8b07fc68
22 changed files with 2670 additions and 96 deletions
+2 -3
View File
@@ -1,13 +1,12 @@
FROM node:20-alpine
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
RUN npm ci
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]
+3 -2
View File
@@ -6,7 +6,8 @@
"scripts": {
"dev": "vite --host 0.0.0.0 --port 5173",
"build": "vite build",
"preview": "vite preview --host 0.0.0.0 --port 4173"
"preview": "vite preview --host 0.0.0.0 --port 4173",
"check": "vite build"
},
"dependencies": {
"cors": "^2.8.6",
@@ -20,4 +21,4 @@
"@vitejs/plugin-react": "^4.4.1",
"vite": "^6.0.7"
}
}
}
+38 -11
View File
@@ -741,7 +741,8 @@ export default function App() {
const [manualDatapointDraft, setManualDatapointDraft] = useState(emptyManualDatapointDraft());
const [scanResults, setScanResults] = useState({ opcua: null, bacnet: null, ipsymcon: null });
const [debugSnapshot, setDebugSnapshot] = useState(null);
const [integrationSettings, setIntegrationSettings] = useState({ mailApiKey: "", whatsappApiKey: "", phoneApiKey: "" });
const [integrationSettings, setIntegrationSettings] = useState({ mailUrl: "https://mailapi.se-inno.de", whatsappUrl: "https://whatsappapi.se-inno.de", voipUrl: "https://voipapi.se-inno.de", mailApiKey: "", whatsappApiKey: "", voipApiKey: "" });
const [integrationStatus, setIntegrationStatus] = useState({ mailConfigured: false, whatsappConfigured: false, voipConfigured: false });
const [runnerJobs, setRunnerJobs] = useState([]);
const [runnerQueue, setRunnerQueue] = useState([]);
const [runnerDraft, setRunnerDraft] = useState({ name: "", selectionId: "", enabled: true, time: "06:00", rangeHours: "24", delimiter: ";", mailEnabled: false, mailTo: "", mailSubject: "Trenddaten", mailMessage: "Automatischer Export aus SE Local Trenddata." });
@@ -1137,12 +1138,16 @@ export default function App() {
}, [canEditAliases, canManageSources, token]);
useEffect(() => {
if (!token || !canManageIntegrations) {
setIntegrationSettings({ mailApiKey: "", whatsappApiKey: "", phoneApiKey: "" });
setIntegrationSettings({ mailUrl: "https://mailapi.se-inno.de", whatsappUrl: "https://whatsappapi.se-inno.de", voipUrl: "https://voipapi.se-inno.de", mailApiKey: "", whatsappApiKey: "", voipApiKey: "" });
setIntegrationStatus({ mailConfigured: false, whatsappConfigured: false, voipConfigured: false });
return;
}
api.getIntegrations(token)
.then((result) => setIntegrationSettings(result.settings || { mailApiKey: "", whatsappApiKey: "", phoneApiKey: "" }))
.then((result) => {
setIntegrationSettings({ mailApiKey: "", whatsappApiKey: "", voipApiKey: "", ...(result.settings || {}) });
setIntegrationStatus({ mailConfigured: Boolean(result.mailConfigured), whatsappConfigured: Boolean(result.whatsappConfigured), voipConfigured: Boolean(result.voipConfigured) });
})
.catch((error) => setBootError(error.message));
}, [canManageIntegrations, token]);
@@ -2086,17 +2091,36 @@ export default function App() {
}
};
const applyIntegrationResult = (result) => {
setIntegrationSettings({ mailApiKey: "", whatsappApiKey: "", voipApiKey: "", ...(result.settings || {}) });
setIntegrationStatus({ mailConfigured: Boolean(result.mailConfigured), whatsappConfigured: Boolean(result.whatsappConfigured), voipConfigured: Boolean(result.voipConfigured) });
};
const saveIntegrationSettings = async () => {
resetFlash();
try {
const result = await api.saveIntegrations(token, integrationSettings);
setIntegrationSettings(result.settings || integrationSettings);
applyIntegrationResult(result);
setSuccessMessage("API-Verbindungen gespeichert.");
} catch (error) {
setBootError(error.message);
}
};
const clearIntegrationKey = async (channel) => {
const labels = { mail: "Mail", whatsapp: "WhatsApp", voip: "Telefon" };
if (!window.confirm(`${labels[channel]} API-Key wirklich entfernen?`)) return;
resetFlash();
try {
const clearField = channel === "mail" ? "clearMailApiKey" : channel === "whatsapp" ? "clearWhatsappApiKey" : "clearVoipApiKey";
const result = await api.saveIntegrations(token, { ...integrationSettings, [clearField]: true });
applyIntegrationResult(result);
setSuccessMessage(`${labels[channel]} API-Key entfernt.`);
} catch (error) {
setBootError(error.message);
}
};
const resetRunnerDraft = () => {
setRunnerDraft({ name: "", selectionId: "", enabled: true, time: "06:00", rangeHours: "24", delimiter: ";", mailEnabled: false, mailTo: "", mailSubject: "Trenddaten", mailMessage: "Automatischer Export aus SE Local Trenddata." });
};
@@ -3484,18 +3508,21 @@ export default function App() {
<div className="form-grid compact-forms">
<label>
<span>Mail API-Key</span>
<input type="password" value={integrationSettings.mailApiKey || ""} onChange={(event) => setIntegrationSettings((current) => ({ ...current, mailApiKey: event.target.value }))} />
<small className="muted">{integrationSettings.mailApiKey ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
<input type="password" autoComplete="new-password" value={integrationSettings.mailApiKey || ""} placeholder={integrationStatus.mailConfigured ? "Key gespeichert - nur zum Ersetzen eingeben" : "API-Key eingeben"} onChange={(event) => setIntegrationSettings((current) => ({ ...current, mailApiKey: event.target.value }))} />
<small className="muted">{integrationStatus.mailConfigured ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
{integrationStatus.mailConfigured ? <button className="ghost-button compact-button danger" type="button" onClick={() => clearIntegrationKey("mail")}>Key entfernen</button> : null}
</label>
<label>
<span>WhatsApp API-Key</span>
<input type="password" value={integrationSettings.whatsappApiKey || ""} onChange={(event) => setIntegrationSettings((current) => ({ ...current, whatsappApiKey: event.target.value }))} />
<small className="muted">{integrationSettings.whatsappApiKey ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
<input type="password" autoComplete="new-password" value={integrationSettings.whatsappApiKey || ""} placeholder={integrationStatus.whatsappConfigured ? "Key gespeichert - nur zum Ersetzen eingeben" : "API-Key eingeben"} onChange={(event) => setIntegrationSettings((current) => ({ ...current, whatsappApiKey: event.target.value }))} />
<small className="muted">{integrationStatus.whatsappConfigured ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
{integrationStatus.whatsappConfigured ? <button className="ghost-button compact-button danger" type="button" onClick={() => clearIntegrationKey("whatsapp")}>Key entfernen</button> : null}
</label>
<label>
<span>Telefon API-Key</span>
<input type="password" value={integrationSettings.phoneApiKey || ""} onChange={(event) => setIntegrationSettings((current) => ({ ...current, phoneApiKey: event.target.value }))} />
<small className="muted">{integrationSettings.phoneApiKey ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
<input type="password" autoComplete="new-password" value={integrationSettings.voipApiKey || ""} placeholder={integrationStatus.voipConfigured ? "Key gespeichert - nur zum Ersetzen eingeben" : "API-Key eingeben"} onChange={(event) => setIntegrationSettings((current) => ({ ...current, voipApiKey: event.target.value }))} />
<small className="muted">{integrationStatus.voipConfigured ? "Key gespeichert" : "Kein Key hinterlegt"}</small>
{integrationStatus.voipConfigured ? <button className="ghost-button compact-button danger" type="button" onClick={() => clearIntegrationKey("voip")}>Key entfernen</button> : null}
</label>
</div>
<button className="primary-button compact-button" type="button" onClick={saveIntegrationSettings}>API-Keys speichern</button>
@@ -3554,7 +3581,7 @@ export default function App() {
<span>aktiv</span>
</label>
<label className="checkbox-field compact-check">
<input type="checkbox" checked={runnerDraft.mailEnabled === true} onChange={(event) => setRunnerDraft((current) => ({ ...current, mailEnabled: event.target.checked }))} disabled={!integrationSettings.mailEnabled && !integrationSettings.mailApiKey} />
<input type="checkbox" checked={runnerDraft.mailEnabled === true} onChange={(event) => setRunnerDraft((current) => ({ ...current, mailEnabled: event.target.checked }))} disabled={!integrationStatus.mailConfigured && !integrationSettings.mailApiKey} />
<span>CSV per Mail verschicken</span>
</label>
<label>