Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
@@ -0,0 +1,105 @@
<script lang="ts">
import { api } from "$lib/api";
import { apiFormError, fieldDescribedBy, fieldInvalid } from "$lib/api-form-error";
import { i18n } from "$lib/i18n";
import {
Alert,
AlertDescription,
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Input,
Label
} from "$lib/components/ui";
let email = $state("");
let error = $state("");
let fieldErrors = $state<Record<string, string>>({});
let loading = $state(false);
let sent = $state(false);
const errorId = "forgot-password-form-error";
async function onSubmit(event: Event) {
event.preventDefault();
error = "";
fieldErrors = {};
loading = true;
try {
await api("/api/auth/forgot-password", {
method: "POST",
body: { email }
});
sent = true;
} catch (err) {
const result = apiFormError(err, i18n.t("auth.forgot.failed"), {
email: ["email", "required", "rate limit"]
});
error = result.message;
fieldErrors = result.fields;
} finally {
loading = false;
}
}
</script>
<Card>
<CardHeader>
<CardTitle level={1}>{i18n.t("auth.forgot.title")}</CardTitle>
<CardDescription>{i18n.t("auth.forgot.description")}</CardDescription>
</CardHeader>
<CardContent>
{#if sent}
<Alert>
<AlertDescription>
<strong class="text-text">{i18n.t("auth.forgot.sentTitle")}</strong>
{" "}
{i18n.t("auth.forgot.sentBody")}
</AlertDescription>
</Alert>
<p class="mt-5 text-center text-sm text-text-muted">
<a href="/login" class="font-medium text-link hover:underline">{i18n.t("auth.forgot.backToSignIn")}</a>
</p>
{:else}
<form
method="dialog"
class="space-y-4"
onsubmit={onSubmit}
aria-busy={loading}
aria-describedby={error ? errorId : undefined}
>
{#if error}
<Alert variant="destructive">
<AlertDescription id={errorId}>{error}</AlertDescription>
</Alert>
{/if}
<div class="space-y-2">
<Label for="email">{i18n.t("common.email")}</Label>
<Input
id="email"
type="email"
name="email"
autocomplete="email"
required
aria-invalid={fieldInvalid(fieldErrors, "email") ??
(error && !Object.keys(fieldErrors).length ? "true" : undefined)}
aria-describedby={fieldDescribedBy(fieldErrors, "email", errorId) ??
(error && !Object.keys(fieldErrors).length ? errorId : undefined)}
bind:value={email}
/>
</div>
<Button type="submit" class="w-full shadow-sm" loading={loading}>
{loading ? i18n.t("auth.forgot.submitting") : i18n.t("auth.forgot.submit")}
</Button>
</form>
<p class="mt-5 text-center text-sm text-text-muted">
<a href="/login" class="font-medium text-link hover:underline">{i18n.t("auth.forgot.backToSignIn")}</a>
</p>
{/if}
</CardContent>
</Card>