feat(frontend): export ui

This commit is contained in:
sam 2024-12-03 20:02:09 +01:00
parent 74222ead45
commit c20831f20d
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
8 changed files with 104 additions and 9 deletions

View file

@ -7,6 +7,7 @@
import Error from "$components/Error.svelte";
import { idTimestamp } from "$lib";
import { DateTime } from "luxon";
import { enhance } from "$app/forms";
type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props();
@ -20,7 +21,7 @@
<div class="row mb-3">
<div class="col-md-9">
<h5>Change your username</h5>
<form method="POST" action="?/changeUsername">
<form method="POST" action="?/changeUsername" use:enhance>
<FormGroup class="mb-3">
<InputGroup class="m-1 mt-3 w-md-75">
<Input

View file

@ -0,0 +1,35 @@
import { apiRequest, fastRequest } from "$api";
import ApiError from "$api/error.js";
import log from "$lib/log.js";
import { DateTime, Duration } from "luxon";
type Export = { url: string | null; expires_at: string | null };
export const load = async ({ fetch, cookies }) => {
const resp = await apiRequest<Export>("GET", "/data-exports", {
fetch,
cookies,
isInternal: true,
});
let canExport = true;
if (resp.expires_at) {
const created = DateTime.fromISO(resp.expires_at).minus(Duration.fromObject({ days: 15 }));
canExport = DateTime.now().diff(created, "seconds").seconds >= 86400;
}
return { url: resp.url, expiresAt: resp.expires_at, canExport };
};
export const actions = {
default: async ({ fetch, cookies }) => {
try {
fastRequest("POST", "/data-exports", { fetch, cookies, isInternal: true });
return { ok: true, error: null };
} catch (e) {
if (e instanceof ApiError) return { ok: false, error: e.obj };
log.error("Error requesting data export:", e);
throw e;
}
},
};

View file

@ -0,0 +1,49 @@
<script lang="ts">
import { DateTime } from "luxon";
import type { ActionData, PageData } from "./$types";
import ErrorAlert from "$components/ErrorAlert.svelte";
import { Icon } from "@sveltestrap/sveltestrap";
import { t } from "$lib/i18n";
import { enhance } from "$app/forms";
type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props();
let expiresAt = $derived.by(() => {
if (!data.expiresAt) return null;
return DateTime.fromISO(data.expiresAt);
});
</script>
<div class="mx-auto w-lg-75">
<h3>{$t("settings.export-title")}</h3>
{#if form?.ok}
<p class="text-success-emphasis">
<Icon name="check-circle-fill" />
{$t("settings.export-request-success")}
</p>
{:else if form?.error}
<ErrorAlert error={form.error} />
{/if}
<p>
{$t("settings.export-info")}
</p>
<form method="POST" use:enhance>
<div class="btn-group">
<button type="submit" class="btn btn-primary" disabled={!data.canExport}>
{$t("settings.export-request-button")}
</button>
{#if data.url}
<a href={data.url} target="_blank" class="btn btn-success">
{$t("settings.export-download")}
{#if expiresAt}
{$t("settings.export-expires-at", { expiresAt: expiresAt.toRelative() })}
{/if}
</a>
{/if}
</div>
</form>
</div>

View file

@ -14,6 +14,7 @@
import SidEditor from "$components/editor/SidEditor.svelte";
import BioEditor from "$components/editor/BioEditor.svelte";
import { PUBLIC_BASE_URL } from "$env/static/public";
import { enhance } from "$app/forms";
type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props();
@ -83,7 +84,7 @@
</div>
<div class="col-md">
<h4>{$t("edit-profile.member-name")}</h4>
<form method="POST" action="?/changeName" class="mb-3">
<form method="POST" action="?/changeName" class="mb-3" use:enhance>
<InputGroup>
<input
name="name"
@ -99,7 +100,7 @@
</form>
<h4>{$t("edit-profile.display-name")}</h4>
<form class="mb-3" method="POST" action="?/changeDisplayName">
<form class="mb-3" method="POST" action="?/changeDisplayName" use:enhance>
<InputGroup>
<input
class="form-control"
@ -117,7 +118,7 @@
</div>
<div class="row mb-3">
<h4>{$t("edit-profile.profile-options-header")}</h4>
<form method="POST" action="?/options">
<form method="POST" action="?/options" use:enhance>
<div class="form-check">
<input
class="form-check-input"
@ -146,7 +147,7 @@
</div>
<div class="row mb-3">
<h4>{$t("edit-profile.bio-tab")}</h4>
<form method="POST" action="?/bio">
<form method="POST" action="?/bio" use:enhance>
<BioEditor bind:value={bio} maxLength={data.meta.limits.bio_length} />
</form>
</div>

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { enhance } from "$app/forms";
import ErrorAlert from "$components/ErrorAlert.svelte";
import { t } from "$lib/i18n";
import type { ActionData } from "./$types";
@ -13,7 +14,7 @@
<ErrorAlert error={form.error} />
{/if}
<form method="POST">
<form method="POST" use:enhance>
<div class="my-3">
<label class="form-label" for="name">{$t("settings.create-member-name-label")}</label>
<input class="form-control" type="text" id="name" name="name" required autocomplete="off" />

View file

@ -11,6 +11,7 @@
import { DateTime, FixedOffsetZone } from "luxon";
import FormStatusMarker from "$components/editor/FormStatusMarker.svelte";
import SidEditor from "$components/editor/SidEditor.svelte";
import { enhance } from "$app/forms";
type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props();
@ -128,7 +129,7 @@
<div class="mt-3">
<h4>{$t("edit-profile.profile-options-header")}</h4>
<form method="POST" action="?/options">
<form method="POST" action="?/options" use:enhance>
<div class="mb-3">
<label class="form-label" for="member-title">{$t("edit-profile.member-header-label")}</label>
<input

View file

@ -3,6 +3,7 @@
import type { ActionData, PageData } from "./$types";
import BioEditor from "$components/editor/BioEditor.svelte";
import { t } from "$lib/i18n";
import { enhance } from "$app/forms";
type Props = { data: PageData; form: ActionData };
let { data, form }: Props = $props();
@ -12,6 +13,6 @@
<h4>{$t("edit-profile.bio-tab")}</h4>
<FormStatusMarker {form} />
<form method="POST">
<form method="POST" use:enhance>
<BioEditor bind:value={bio} maxLength={data.meta.limits.bio_length} />
</form>