Files
omsorg/omsorgapp/src/modules/settings/ResetUserPasswordDialog.jsx
T
Felix KemmlerandClaude Sonnet 5 598dfcd38a
Docker-Images bauen und veröffentlichen / build (, omsorgCore/Dockerfile, omsorgcore) (push) Successful in 3s
Docker-Images bauen und veröffentlichen / build (, omsorgWeb/Dockerfile, omsorgweb) (push) Failing after 4s
Docker-Images bauen und veröffentlichen / build (VITE_OMSORG_CORE_URL=${{ vars.OMSORG_CORE_PUBLIC_URL }}, omsorgapp/Dockerfile, omsorgapp) (push) Successful in 3s
Migrate omsorgapp to browser SPA, add Docker/CI build setup
- omsorgapp: drop Electron, run as a plain Vite/React browser app; refresh
  token moves to an HttpOnly cookie (omsorgCore), CORS added for the new
  browser origin, document download/preview switched to Blob-based browser
  APIs.
- Add Dockerfiles for omsorgCore, omsorgapp, and omsorgWeb, a docker-compose.yml
  wiring Postgres/MySQL/all three apps together, and a Gitea Actions workflow
  that builds and pushes images to the repo's container registry on push to
  main and on version tags.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 17:42:45 +02:00

129 lines
4.1 KiB
React

import { useState } from "react";
import { FormActions } from "../employees/EmployeeForm";
import { useAuth } from "../../app/AuthContext";
import ModalPortal from "../../components/ui/ModalPortal";
const PIN_VALIDITY_OPTIONS = [1, 3, 7, 14];
const DEFAULT_PIN_VALIDITY_DAYS = 7;
function errorMessage(result) {
if (result.status === 403) {
return "Keine Berechtigung, Passwörter zurückzusetzen.";
}
if (result.status === 404) {
return "Nutzer nicht gefunden.";
}
if (result.status === 400) {
return typeof result.data === "string" ? result.data : "Eingaben prüfen.";
}
return "Passwort konnte nicht zurückgesetzt werden.";
}
export default function ResetUserPasswordDialog({ user, onClose, onDone }) {
const { passwordMinLength } = useAuth();
const [mode, setMode] = useState("Invite");
const [pinValidityDays, setPinValidityDays] = useState(DEFAULT_PIN_VALIDITY_DAYS);
const [initialPassword, setInitialPassword] = useState("");
const [isSaving, setIsSaving] = useState(false);
const [error, setError] = useState(null);
async function handleSubmit(event) {
event.preventDefault();
if (mode === "Direct" && initialPassword.trim().length < passwordMinLength) {
setError(`Initiales Passwort muss mindestens ${passwordMinLength} Zeichen lang sein.`);
return;
}
setIsSaving(true);
setError(null);
const result = await window.omsorg.users.resetPassword(user.id, {
mode,
initialPassword: mode === "Direct" ? initialPassword : null,
pinValidityDays: mode === "Invite" ? pinValidityDays : null,
});
setIsSaving(false);
if (!result.ok) {
setError(errorMessage(result));
return;
}
onDone();
}
return (
<ModalPortal>
<div className="modal-overlay" role="dialog" aria-modal="true" aria-label="Passwort zurücksetzen">
<div className="modal-panel">
<h2>Passwort für {user.username} zurücksetzen</h2>
<form onSubmit={handleSubmit}>
<fieldset className="form-field">
<legend>Zugang</legend>
<label>
<input
type="radio"
name="reset-mode"
value="Invite"
checked={mode === "Invite"}
onChange={() => setMode("Invite")}
/>
Einladung per Mail (Nutzer setzt eigenes Passwort)
</label>
<label>
<input
type="radio"
name="reset-mode"
value="Direct"
checked={mode === "Direct"}
onChange={() => setMode("Direct")}
/>
Passwort direkt vergeben (muss beim nächsten Login geändert werden)
</label>
</fieldset>
{mode === "Invite" && (
<label className="form-field">
<span>PIN gültig für</span>
<select
value={pinValidityDays}
onChange={(event) => setPinValidityDays(Number(event.target.value))}
>
{PIN_VALIDITY_OPTIONS.map((days) => (
<option key={days} value={days}>
{days} {days === 1 ? "Tag" : "Tage"}
{days === DEFAULT_PIN_VALIDITY_DAYS ? " (Standard)" : ""}
</option>
))}
</select>
</label>
)}
{mode === "Direct" && (
<label className="form-field">
<span>Neues initiales Passwort</span>
<input
type="text"
value={initialPassword}
onChange={(event) => setInitialPassword(event.target.value)}
placeholder={`Mindestens ${passwordMinLength} Zeichen`}
/>
</label>
)}
{error && <p className="login-error form-error">{error}</p>}
<FormActions onCancel={onClose} isSaving={isSaving} submitLabel="Zurücksetzen" />
</form>
</div>
</div>
</ModalPortal>
);
}