mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-30 19:26:37 +00:00
feat: add user display name field (#898)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import AppConfigService from '$lib/services/app-config-service';
|
||||
import UserService from '$lib/services/user-service';
|
||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import { setLocaleForLibraries } from '$lib/utils/locale.util';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const ssr = false;
|
||||
@@ -29,6 +30,8 @@ export const load: LayoutLoad = async () => {
|
||||
appConfigStore.set(appConfig);
|
||||
}
|
||||
|
||||
await setLocaleForLibraries();
|
||||
|
||||
return {
|
||||
user,
|
||||
appConfig
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { axiosErrorToast } from '$lib/utils/error-util';
|
||||
import { preventDefault } from '$lib/utils/event-util';
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { emptyToUndefined } from '$lib/utils/zod-util';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
@@ -26,12 +27,14 @@
|
||||
} = $props();
|
||||
|
||||
let isLoading = $state(false);
|
||||
let hasManualDisplayNameEdit = $state(!!account.displayName);
|
||||
|
||||
const userService = new UserService();
|
||||
|
||||
const formSchema = z.object({
|
||||
firstName: z.string().min(1).max(50),
|
||||
lastName: z.string().max(50).optional(),
|
||||
lastName: emptyToUndefined(z.string().max(50).optional()),
|
||||
displayName: z.string().max(100),
|
||||
username: z
|
||||
.string()
|
||||
.min(2)
|
||||
@@ -44,6 +47,14 @@
|
||||
|
||||
const { inputs, ...form } = createForm<FormSchema>(formSchema, account);
|
||||
|
||||
function onNameInput() {
|
||||
if (!hasManualDisplayNameEdit) {
|
||||
$inputs.displayName.value = `${$inputs.firstName.value}${
|
||||
$inputs.lastName?.value ? ' ' + $inputs.lastName.value : ''
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
const data = form.validate();
|
||||
if (!data) return;
|
||||
@@ -68,7 +79,6 @@
|
||||
</script>
|
||||
|
||||
<form onsubmit={preventDefault(onSubmit)} class="space-y-6">
|
||||
<!-- Profile Picture Section -->
|
||||
<ProfilePictureSettings
|
||||
{userId}
|
||||
{isLdapUser}
|
||||
@@ -76,31 +86,32 @@
|
||||
resetCallback={resetProfilePicture}
|
||||
/>
|
||||
|
||||
<!-- Divider -->
|
||||
<hr class="border-border" />
|
||||
|
||||
<!-- User Information -->
|
||||
<fieldset disabled={userInfoInputDisabled}>
|
||||
<div>
|
||||
<div class="flex flex-col gap-3 sm:flex-row">
|
||||
<div class="w-full">
|
||||
<FormInput label={m.first_name()} bind:input={$inputs.firstName} />
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<FormInput label={m.last_name()} bind:input={$inputs.lastName} />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<FormInput label={m.first_name()} bind:input={$inputs.firstName} onInput={onNameInput} />
|
||||
</div>
|
||||
<div class="mt-3 flex flex-col gap-3 sm:flex-row">
|
||||
<div class="w-full">
|
||||
<FormInput label={m.email()} bind:input={$inputs.email} />
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<FormInput label={m.username()} bind:input={$inputs.username} />
|
||||
</div>
|
||||
<div>
|
||||
<FormInput label={m.last_name()} bind:input={$inputs.lastName} onInput={onNameInput} />
|
||||
</div>
|
||||
<div>
|
||||
<FormInput
|
||||
label={m.display_name()}
|
||||
bind:input={$inputs.displayName}
|
||||
onInput={() => (hasManualDisplayNameEdit = true)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormInput label={m.username()} bind:input={$inputs.username} />
|
||||
</div>
|
||||
<div>
|
||||
<FormInput label={m.email()} bind:input={$inputs.email} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-2">
|
||||
<div class="flex justify-end pt-4">
|
||||
<Button {isLoading} type="submit">{m.save()}</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -120,7 +120,12 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<CollapsibleCard id="application-configuration-images" icon={LucideImage} title={m.images()}>
|
||||
<CollapsibleCard
|
||||
id="application-configuration-images"
|
||||
icon={LucideImage}
|
||||
title={m.images()}
|
||||
description={m.configure_application_images()}
|
||||
>
|
||||
<UpdateApplicationImages callback={updateImages} />
|
||||
</CollapsibleCard>
|
||||
</div>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
ldapAttributeUserEmail: z.string().min(1),
|
||||
ldapAttributeUserFirstName: z.string().min(1),
|
||||
ldapAttributeUserLastName: z.string().min(1),
|
||||
ldapAttributeUserDisplayName: z.string().min(1),
|
||||
ldapAttributeUserProfilePicture: z.string(),
|
||||
ldapAttributeGroupMember: z.string(),
|
||||
ldapAttributeGroupUniqueIdentifier: z.string().min(1),
|
||||
@@ -159,6 +160,11 @@
|
||||
placeholder="sn"
|
||||
bind:input={$inputs.ldapAttributeUserLastName}
|
||||
/>
|
||||
<FormInput
|
||||
label={m.display_name_attribute()}
|
||||
placeholder="displayName"
|
||||
bind:input={$inputs.ldapAttributeUserDisplayName}
|
||||
/>
|
||||
<FormInput
|
||||
label={m.user_profile_picture_attribute()}
|
||||
description={m.the_value_of_this_attribute_can_either_be_a_url_binary_or_base64_encoded_image()}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import SwitchWithLabel from '$lib/components/form/switch-with-label.svelte';
|
||||
import FormInput from '$lib/components/form/form-input.svelte';
|
||||
import SwitchWithLabel from '$lib/components/form/switch-with-label.svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||
import type { User, UserCreate } from '$lib/types/user.type';
|
||||
import { preventDefault } from '$lib/utils/event-util';
|
||||
import { createForm } from '$lib/utils/form-util';
|
||||
import { emptyToUndefined } from '$lib/utils/zod-util';
|
||||
import { z } from 'zod/v4';
|
||||
|
||||
let {
|
||||
@@ -19,10 +20,12 @@
|
||||
|
||||
let isLoading = $state(false);
|
||||
let inputDisabled = $derived(!!existingUser?.ldapId && $appConfigStore.ldapEnabled);
|
||||
let hasManualDisplayNameEdit = $state(!!existingUser?.displayName);
|
||||
|
||||
const user = {
|
||||
firstName: existingUser?.firstName || '',
|
||||
lastName: existingUser?.lastName || '',
|
||||
displayName: existingUser?.displayName || '',
|
||||
email: existingUser?.email || '',
|
||||
username: existingUser?.username || '',
|
||||
isAdmin: existingUser?.isAdmin || false,
|
||||
@@ -31,7 +34,8 @@
|
||||
|
||||
const formSchema = z.object({
|
||||
firstName: z.string().min(1).max(50),
|
||||
lastName: z.string().max(50),
|
||||
lastName: emptyToUndefined(z.string().max(50).optional()),
|
||||
displayName: z.string().max(100),
|
||||
username: z
|
||||
.string()
|
||||
.min(2)
|
||||
@@ -53,15 +57,29 @@
|
||||
if (success && !existingUser) form.reset();
|
||||
isLoading = false;
|
||||
}
|
||||
function onNameInput() {
|
||||
if (!hasManualDisplayNameEdit) {
|
||||
$inputs.displayName.value = `${$inputs.firstName.value}${
|
||||
$inputs.lastName?.value ? ' ' + $inputs.lastName.value : ''
|
||||
}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form onsubmit={preventDefault(onSubmit)}>
|
||||
<fieldset disabled={inputDisabled}>
|
||||
<div class="grid grid-cols-1 items-start gap-5 md:grid-cols-2">
|
||||
<FormInput label={m.first_name()} bind:input={$inputs.firstName} />
|
||||
<FormInput label={m.last_name()} bind:input={$inputs.lastName} />
|
||||
<FormInput label={m.first_name()} oninput={onNameInput} bind:input={$inputs.firstName} />
|
||||
<FormInput label={m.last_name()} oninput={onNameInput} bind:input={$inputs.lastName} />
|
||||
<FormInput
|
||||
label={m.display_name()}
|
||||
oninput={() => (hasManualDisplayNameEdit = true)}
|
||||
bind:input={$inputs.displayName}
|
||||
/>
|
||||
<FormInput label={m.username()} bind:input={$inputs.username} />
|
||||
<FormInput label={m.email()} bind:input={$inputs.email} />
|
||||
</div>
|
||||
<div class="mt-5 grid grid-cols-1 items-start gap-5 md:grid-cols-2">
|
||||
<SwitchWithLabel
|
||||
id="admin-privileges"
|
||||
label={m.admin_privileges()}
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
columns={[
|
||||
{ label: m.first_name(), sortColumn: 'firstName' },
|
||||
{ label: m.last_name(), sortColumn: 'lastName' },
|
||||
{ label: m.display_name(), sortColumn: 'displayName' },
|
||||
{ label: m.email(), sortColumn: 'email' },
|
||||
{ label: m.username(), sortColumn: 'username' },
|
||||
{ label: m.role(), sortColumn: 'isAdmin' },
|
||||
@@ -114,6 +115,7 @@
|
||||
{#snippet rows({ item })}
|
||||
<Table.Cell>{item.firstName}</Table.Cell>
|
||||
<Table.Cell>{item.lastName}</Table.Cell>
|
||||
<Table.Cell>{item.displayName}</Table.Cell>
|
||||
<Table.Cell>{item.email}</Table.Cell>
|
||||
<Table.Cell>{item.username}</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
Reference in New Issue
Block a user