feat(frontend): export ui
This commit is contained in:
		
							parent
							
								
									74222ead45
								
							
						
					
					
						commit
						c20831f20d
					
				
					 8 changed files with 104 additions and 9 deletions
				
			
		|  | @ -112,7 +112,13 @@ | |||
| 		"create-member-name-label": "Member name", | ||||
| 		"auth-remove-method": "Remove", | ||||
| 		"force-log-out-warning": "Make sure you're still able to log in before using this!", | ||||
| 		"force-log-out-confirmation": "Are you sure you want to log out from all devices? If you just want to log out from this device, click the \"Log out\" button on your settings page." | ||||
| 		"force-log-out-confirmation": "Are you sure you want to log out from all devices? If you just want to log out from this device, click the \"Log out\" button on your settings page.", | ||||
| 		"export-request-success": "Successfully requested a new export! Please note that it may take a few minutes to complete, especially if you have a lot of members.", | ||||
| 		"export-title": "Request a copy of your data", | ||||
| 		"export-info": "You can request a copy of your data once every 24 hours. Exports are stored for 15 days (a little over two weeks) and then deleted.", | ||||
| 		"export-expires-at": "(expires {{expiresAt}})", | ||||
| 		"export-download": "Download export", | ||||
| 		"export-request-button": "Request a new export" | ||||
| 	}, | ||||
| 	"yes": "Yes", | ||||
| 	"no": "No", | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										35
									
								
								Foxnouns.Frontend/src/routes/settings/export/+page.server.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Foxnouns.Frontend/src/routes/settings/export/+page.server.ts
									
										
									
									
									
										Normal 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; | ||||
| 		} | ||||
| 	}, | ||||
| }; | ||||
							
								
								
									
										49
									
								
								Foxnouns.Frontend/src/routes/settings/export/+page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								Foxnouns.Frontend/src/routes/settings/export/+page.svelte
									
										
									
									
									
										Normal 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> | ||||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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" /> | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue