Files
pv-pulse-angular-app/src/app/features/settings/settings.component.html

230 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="settings-page">
<header class="settings-header">
<button type="button" class="btn-back" (click)="goBack()" aria-label="Zurück zum Dashboard">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
</svg>
Zurück
</button>
<h1>
<svg class="settings-header__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 15.5a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm7.43-2.03c.04-.32.07-.65.07-.97s-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11.5c-.04.34-.07.67-.07 1s.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.58 1.69-.98l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.66z"/>
</svg>
Einstellungen
</h1>
</header>
<form [formGroup]="form" (ngSubmit)="save()" class="settings-form" novalidate>
<!-- ── API-Konfiguration ─────────────────────────────── -->
<section class="settings-section">
<h2 class="section-title">API-Konfiguration</h2>
<div class="form-group">
<label for="apiBaseUrl">API Basis-URL</label>
<input
id="apiBaseUrl"
type="url"
formControlName="apiBaseUrl"
placeholder="https://api.example.com/api"
autocomplete="off"
/>
@if (form.controls.apiBaseUrl.invalid && form.controls.apiBaseUrl.touched) {
<span class="field-error">Pflichtfeld bitte eine gültige URL eingeben</span>
}
</div>
<div class="form-group form-group--checkbox">
<input id="useMocks" type="checkbox" formControlName="useMocks" />
<label for="useMocks">Mock-Modus aktiv (kein echter API-Aufruf)</label>
</div>
<div class="form-group">
<label for="pollingIntervalMs">Abfrageintervall Live-Daten (ms)</label>
<input
id="pollingIntervalMs"
type="number"
formControlName="pollingIntervalMs"
min="1000"
step="500"
/>
@if (form.controls.pollingIntervalMs.invalid && form.controls.pollingIntervalMs.touched) {
<span class="field-error">Minimum 1000 ms</span>
}
</div>
</section>
<!-- ── Endpunkte ────────────────────────────────────── -->
<section class="settings-section">
<h2 class="section-title">Endpunkte</h2>
<p class="section-hint">Verwende <code>&#123;id&#125;</code> als Platzhalter f&#252;r die Anlagen-ID</p>
<div class="form-group">
<label for="endpointPlants">Anlagenliste</label>
<input
id="endpointPlants"
type="text"
formControlName="endpointPlants"
placeholder="/plants"
autocomplete="off"
/>
@if (form.controls.endpointPlants.invalid && form.controls.endpointPlants.touched) {
<span class="field-error">Pflichtfeld</span>
}
</div>
<div class="form-group">
<label for="endpointLiveData">Live-Daten</label>
<input
id="endpointLiveData"
type="text"
formControlName="endpointLiveData"
placeholder="/plants/{id}/live"
autocomplete="off"
/>
@if (form.controls.endpointLiveData.invalid && form.controls.endpointLiveData.touched) {
<span class="field-error">Pflichtfeld</span>
}
</div>
<div class="form-group">
<label for="endpointHistory">Historien-Daten</label>
<input
id="endpointHistory"
type="text"
formControlName="endpointHistory"
placeholder="/plants/{id}/history"
autocomplete="off"
/>
@if (form.controls.endpointHistory.invalid && form.controls.endpointHistory.touched) {
<span class="field-error">Pflichtfeld</span>
}
</div>
</section>
<!-- ── Authentifizierung ─────────────────────────────── -->
<section class="settings-section">
<h2 class="section-title">Authentifizierung</h2>
<div class="form-group">
<label for="authEndpoint">Auth-Endpunkt (relativer Pfad)</label>
<input
id="authEndpoint"
type="text"
formControlName="authEndpoint"
placeholder="/auth/token"
autocomplete="off"
/>
@if (form.controls.authEndpoint.invalid && form.controls.authEndpoint.touched) {
<span class="field-error">Pflichtfeld</span>
}
</div>
<div class="form-group">
<label for="username">Benutzername</label>
<input
id="username"
type="text"
formControlName="username"
autocomplete="username"
/>
</div>
<div class="form-group">
<label for="password">Passwort</label>
<div class="input-with-action">
<input
id="password"
[type]="showPassword() ? 'text' : 'password'"
formControlName="password"
autocomplete="current-password"
/>
<button
type="button"
class="btn-icon"
(click)="showPassword.set(!showPassword())"
[attr.aria-label]="showPassword() ? 'Passwort verbergen' : 'Passwort anzeigen'"
title="{{ showPassword() ? 'Passwort verbergen' : 'Passwort anzeigen' }}"
>
@if (showPassword()) {
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20">
<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46A11.804 11.804 0 0 0 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78 3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>
</svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="20" height="20">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8a3 3 0 1 0 0 6 3 3 0 0 0 0-6z"/>
</svg>
}
</button>
</div>
</div>
<!-- Token-Status -->
<div class="token-card">
<div class="token-card__status">
@if (token()) {
<div class="token-indicator token-indicator--active">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
<span>Token aktiv</span>
</div>
<code class="token-value">{{ maskedToken }}</code>
<button type="button" class="btn-ghost btn-sm" (click)="clearToken()">Löschen</button>
} @else {
<div class="token-indicator token-indicator--missing">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
</svg>
<span>Kein Token gesetzt</span>
</div>
}
</div>
@if (tokenStatus() === 'loading') {
<p class="token-card__msg token-card__msg--info">Token wird abgerufen…</p>
}
@if (tokenStatus() === 'success') {
<p class="token-card__msg token-card__msg--success">✓ Token erfolgreich abgerufen</p>
}
@if (tokenStatus() === 'error') {
<p class="token-card__msg token-card__msg--error">✗ {{ tokenError() }}</p>
}
</div>
<button
type="button"
class="btn-secondary"
(click)="fetchToken()"
[disabled]="!form.controls.username.value || !form.controls.password.value || tokenStatus() === 'loading'"
>
@if (tokenStatus() === 'loading') {
<span class="btn-spinner"></span>
Token wird abgerufen…
} @else {
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
<path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4l4-4-4-4v4h-4.35zM7 16c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"/>
</svg>
Token abrufen
}
</button>
</section>
<!-- ── Aktionen ──────────────────────────────────────── -->
<div class="settings-actions">
@if (saveSuccess()) {
<span class="save-success">✓ Einstellungen gespeichert</span>
}
<button type="button" class="btn-secondary" (click)="goBack()">Abbrechen</button>
<button type="submit" class="btn-primary" [disabled]="form.invalid">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
<path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/>
</svg>
Einstellungen speichern
</button>
</div>
</form>
</div>