Login: Passwort-Auge, aenderbare Serveradresse, laenger stehende Fehlermeldung
All checks were successful
Build APK / build-apk (push) Has been skipped
All checks were successful
Build APK / build-apk (push) Has been skipped
- Auge-Symbol im Passwortfeld. Haben alle anderen AWL-Apps seit 06.07.2026; NetDiag war die letzte ohne. Grund: bei falschem Tastaturlayout tippt man sich sonst blind fest. Der Typ wird imperativ am Element gesetzt, weil Svelte 5 ein dynamisches type-Attribut an einem Feld mit bind:value verbietet; type="button" am Schalter ist Pflicht, sonst schickt er das Formular ab. - Serveradresse laesst sich in den Einstellungen aendern. Bisher stand sie dort nur als Text — eine falsche Adresse liess sich nur durch Abmelden korrigieren. Ein Wechsel meldet bewusst ab, weil das Token nur beim bisherigen Server gilt. - Login-Fehler bleiben 8 s stehen statt 3 s und zeigen die Servermeldung im Wortlaut. Bei 429 steht dort die Wartezeit der Brute-Force-Bremse, bei 403 der Grund. Im Emulator (Pixel 6 / Android 14) gegen das Test-Dolibarr geprueft: Ein- und Ausblenden des Passworts, Anmeldung ueber awlauth, Auftragsliste, Einstellungen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3c95ff6b07
commit
4189448c56
2 changed files with 125 additions and 12 deletions
|
|
@ -2,7 +2,8 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import AppHeader from '$lib/components/AppHeader.svelte';
|
||||
import { auth } from '$lib/auth.svelte';
|
||||
import { getServerUrl } from '$lib/api';
|
||||
import { getServerUrl, setServerUrl } from '$lib/api';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { sync } from '$lib/sync.svelte';
|
||||
import { toast } from '$lib/toast.svelte';
|
||||
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
||||
|
|
@ -62,6 +63,33 @@
|
|||
sync.stop();
|
||||
goto('/login/');
|
||||
}
|
||||
|
||||
/* --- Server-Adresse ändern ------------------------------------------- */
|
||||
// Nur auf dem Gerät sinnvoll: im Browser läuft alles über den Vite-Proxy.
|
||||
const canEditServer = Capacitor.isNativePlatform();
|
||||
let editServer = $state(false);
|
||||
let serverInput = $state(getServerUrl());
|
||||
|
||||
/**
|
||||
* Neue Server-Adresse übernehmen. Das Token gilt nur beim alten Server,
|
||||
* deshalb wird bewusst abgemeldet statt so zu tun, als liefe alles weiter.
|
||||
*/
|
||||
async function saveServer() {
|
||||
const url = serverInput.trim();
|
||||
if (!url) {
|
||||
toast.show('Server-Adresse fehlt', 'error');
|
||||
return;
|
||||
}
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
toast.show('Adresse muss mit http:// oder https:// beginnen', 'error', 5000);
|
||||
return;
|
||||
}
|
||||
await setServerUrl(url);
|
||||
await auth.logout();
|
||||
sync.stop();
|
||||
toast.show('Server geändert — bitte neu anmelden', 'info', 5000);
|
||||
goto('/login/');
|
||||
}
|
||||
</script>
|
||||
|
||||
<AppHeader title="Einstellungen" back />
|
||||
|
|
@ -113,9 +141,54 @@
|
|||
<section class="rounded-lg bg-zinc-900 p-4">
|
||||
<h2 class="mb-2 text-sm font-semibold text-zinc-300">Synchronisierung</h2>
|
||||
<p class="text-sm text-zinc-300">{syncLabel}</p>
|
||||
{#if editServer}
|
||||
<div class="mt-2 flex flex-col gap-2">
|
||||
<input
|
||||
class="w-full rounded-lg border border-zinc-700 bg-zinc-800 px-3 py-2 text-sm"
|
||||
type="url"
|
||||
inputmode="url"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
placeholder="https://dolibarr.example.de"
|
||||
bind:value={serverInput}
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex-1 rounded bg-zinc-800 px-3 py-2 text-sm active:bg-zinc-700"
|
||||
onclick={() => {
|
||||
editServer = false;
|
||||
serverInput = getServerUrl();
|
||||
}}
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
class="flex-1 rounded bg-sky-700 px-3 py-2 text-sm font-semibold text-white active:bg-sky-800"
|
||||
onclick={saveServer}
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-zinc-500">
|
||||
Ein Wechsel meldet ab — das Token gilt nur beim bisherigen Server.
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="mt-0.5 break-all text-xs text-zinc-500">
|
||||
Server: {getServerUrl() || '(Browser-Proxy)'}
|
||||
{#if canEditServer}
|
||||
<button
|
||||
class="ml-1 text-sky-400 underline active:text-sky-300"
|
||||
onclick={() => {
|
||||
serverInput = getServerUrl();
|
||||
editServer = true;
|
||||
}}
|
||||
>
|
||||
ändern
|
||||
</button>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<button
|
||||
class="mt-3 w-full rounded bg-zinc-800 px-3 py-2 text-sm active:bg-zinc-700 disabled:opacity-50"
|
||||
onclick={() => sync.syncManual()}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import { getServerUrl, setServerUrl, ApiError } from '$lib/api';
|
||||
import { toast } from '$lib/toast.svelte';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
import { Eye, EyeOff } from 'lucide-svelte';
|
||||
|
||||
let server = $state(getServerUrl());
|
||||
let loginName = $state('');
|
||||
|
|
@ -14,6 +15,21 @@
|
|||
// Server-URL nur auf dem Gerät nötig (im Browser läuft der Vite-Proxy)
|
||||
const needsServer = Capacitor.isNativePlatform();
|
||||
|
||||
/**
|
||||
* Passwort ein-/ausblenden.
|
||||
*
|
||||
* Der Typ wird imperativ am Element gesetzt: Svelte 5 verbietet ein
|
||||
* dynamisches `type` an einem Feld mit `bind:value`. Grund für den Schalter:
|
||||
* bei falschem Tastaturlayout tippt man sich sonst blind fest.
|
||||
*/
|
||||
let pwEl: HTMLInputElement;
|
||||
let pwVisible = $state(false);
|
||||
|
||||
function togglePw() {
|
||||
pwVisible = !pwVisible;
|
||||
if (pwEl) pwEl.type = pwVisible ? 'text' : 'password';
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
e.preventDefault();
|
||||
if (busy) return;
|
||||
|
|
@ -28,7 +44,14 @@
|
|||
toast.show('Angemeldet', 'success');
|
||||
goto('/auftraege/');
|
||||
} catch (err) {
|
||||
toast.show(err instanceof ApiError ? err.message : 'Anmeldung fehlgeschlagen', 'error');
|
||||
// Servermeldung im Wortlaut zeigen und stehen lassen — bei 429 steht dort
|
||||
// die Wartezeit, bei 403 der Grund. Eine Meldung, die nach 3 s verschwindet,
|
||||
// ist auf der Baustelle wertlos.
|
||||
toast.show(
|
||||
err instanceof ApiError ? err.message : 'Anmeldung fehlgeschlagen',
|
||||
'error',
|
||||
8000,
|
||||
);
|
||||
} finally {
|
||||
busy = false;
|
||||
}
|
||||
|
|
@ -66,13 +89,30 @@
|
|||
</label>
|
||||
<label class="flex flex-col gap-1 text-sm">
|
||||
<span class="text-zinc-400">Passwort</span>
|
||||
<div class="relative">
|
||||
<input
|
||||
class="rounded-lg border border-zinc-700 bg-zinc-800 px-3 py-2"
|
||||
class="w-full rounded-lg border border-zinc-700 bg-zinc-800 py-2 pl-3 pr-12"
|
||||
type="password"
|
||||
bind:this={pwEl}
|
||||
bind:value={password}
|
||||
autocomplete="current-password"
|
||||
required
|
||||
/>
|
||||
<!-- type="button" ist Pflicht: sonst schickt der Schalter das Formular ab -->
|
||||
<button
|
||||
type="button"
|
||||
class="absolute right-0 top-0 flex h-full w-12 items-center justify-center text-zinc-400 active:text-zinc-200"
|
||||
onclick={togglePw}
|
||||
aria-label={pwVisible ? 'Passwort verbergen' : 'Passwort anzeigen'}
|
||||
aria-pressed={pwVisible}
|
||||
>
|
||||
{#if pwVisible}
|
||||
<EyeOff size={20} />
|
||||
{:else}
|
||||
<Eye size={20} />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
<button
|
||||
class="mt-2 rounded-lg bg-sky-600 py-2.5 font-semibold text-white active:bg-sky-700 disabled:opacity-50"
|
||||
|
|
|
|||
Loading…
Reference in a new issue