141 lines
3.1 KiB
Svelte
141 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
import api, { currentUser, logout } from '$lib/api';
|
|
import { type UpendApiError } from '@upnd/upend/api';
|
|
import { i18n } from '$lib/i18n';
|
|
import Icon from '$lib/components/utils/Icon.svelte';
|
|
import { slide } from 'svelte/transition';
|
|
import Modal from '$lib/components/layout/Modal.svelte';
|
|
|
|
export let open = false;
|
|
|
|
let changePasswordModal = false;
|
|
let currentPassword = '';
|
|
let newPassword = '';
|
|
let newPasswordConfirm = '';
|
|
|
|
async function changePassword() {
|
|
try {
|
|
const result = await api.authenticate(
|
|
{ username: $currentUser!, password: currentPassword },
|
|
'key'
|
|
);
|
|
if (result) {
|
|
await api.register({ username: $currentUser!, password: newPassword });
|
|
alert($i18n.t('Password changed successfully').toString());
|
|
changePasswordModal = false;
|
|
}
|
|
} catch (e: unknown) {
|
|
alert(
|
|
$i18n.t('Error authenticating: {error}', { error: (e as UpendApiError).message }).toString()
|
|
);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<svelte:body
|
|
on:click={() => {
|
|
open = false;
|
|
}}
|
|
/>
|
|
|
|
{#if open}
|
|
<!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
|
|
<div class="user-dropdown" transition:slide on:click|stopPropagation>
|
|
<div class="user">
|
|
<Icon plain name="user" />
|
|
{$currentUser || '???'}
|
|
</div>
|
|
<button on:click={() => (changePasswordModal = true)}>
|
|
<Icon name="lock" />{$i18n.t('Change password')}</button
|
|
>
|
|
<button on:click={() => logout()}> <Icon name="log-out" />{$i18n.t('Log out')}</button>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if changePasswordModal}
|
|
<Modal on:close={() => (changePasswordModal = false)}>
|
|
<h2>
|
|
<Icon name="lock" />
|
|
{$i18n.t('Change password')}
|
|
</h2>
|
|
<form class="change-password">
|
|
<label>
|
|
<span>{$i18n.t('Current password')}</span>
|
|
<input type="password" bind:value={currentPassword} required />
|
|
</label>
|
|
<label>
|
|
<span>{$i18n.t('New password')}</span>
|
|
<input type="password" bind:value={newPassword} required />
|
|
</label>
|
|
<label>
|
|
<span>{$i18n.t('Confirm new password')}</span>
|
|
<input type="password" bind:value={newPasswordConfirm} required />
|
|
</label>
|
|
<button
|
|
type="submit"
|
|
on:click={() => changePassword()}
|
|
disabled={newPassword !== newPasswordConfirm || !newPassword}
|
|
>{$i18n.t('Change password')}</button
|
|
>
|
|
</form>
|
|
</Modal>
|
|
{/if}
|
|
|
|
<style>
|
|
.user-dropdown {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
|
|
background: var(--background);
|
|
border-radius: 4px;
|
|
border: 1px solid var(--foreground);
|
|
padding: 0.5em;
|
|
position: absolute;
|
|
top: 3.5rem;
|
|
right: 0.5rem;
|
|
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.5);
|
|
z-index: 99;
|
|
}
|
|
|
|
.user {
|
|
font-weight: 500;
|
|
margin: 0 1rem;
|
|
}
|
|
|
|
button,
|
|
.user {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
h2 {
|
|
text-align: center;
|
|
margin: 0 0 1rem 0;
|
|
}
|
|
|
|
.change-password {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
align-items: center;
|
|
gap: 0.5rem 1rem;
|
|
|
|
& label {
|
|
display: contents;
|
|
|
|
& span {
|
|
text-align: right;
|
|
}
|
|
}
|
|
|
|
& button {
|
|
grid-column: span 2;
|
|
justify-content: center;
|
|
justify-self: center;
|
|
font-weight: 500;
|
|
margin-top: 1rem;
|
|
}
|
|
}
|
|
</style>
|