diff --git a/src/Pages/ShowBasicData.vue b/src/Pages/ShowBasicData.vue index 5da93da4a4f1a8b909a4039ddc99b84df6a52fc6..542c741c8a5d8d321911c9b8c91eeb6dcd5afacc 100644 --- a/src/Pages/ShowBasicData.vue +++ b/src/Pages/ShowBasicData.vue @@ -246,22 +246,7 @@ :errors="owners.errors" edit-permissions="program.edit__show__owners" > - <ComboBoxSimple - v-model="owners.value" - :disabled="disabled" - :search-provider="searchUsers" - > - <template #default="{ choice, ...itemAttrs }"> - <li v-bind="itemAttrs"> - <UserPreview :user="choice as SteeringUser" transparent /> - </li> - </template> - <template #selected="{ deselect }"> - <template v-for="user in owners.value" :key="user.id"> - <UserPreview :user="user" :removable="!disabled" @remove="deselect(user)" /> - </template> - </template> - </ComboBoxSimple> + <AUserSelector v-model="owners.value" :disabled="disabled" /> </FormGroup> </AFieldset> @@ -395,7 +380,6 @@ import { useUserStore, } from '@/stores' import { useBreadcrumbs } from '@/stores/nav' -import { SteeringUser } from '@/stores/auth' import { sanitizeHTML } from '@/util' import PageHeader from '@/components/PageHeader.vue' @@ -408,11 +392,11 @@ import SafeHTML from '@/components/generic/SafeHTML' import ALinkCollectionEditor from '@/components/generic/ALinkCollectionEditor.vue' import AHTMLEditor from '@/components/generic/AHTMLEditor.vue' import ComboBoxSimple from '@/components/ComboBoxSimple.vue' -import UserPreview from '@/components/UserPreview.vue' import Tag from '@/components/generic/Tag.vue' import ImagePicker from '@/components/images/ImagePicker.vue' import AFieldset from '@/components/generic/AFieldset.vue' import APlaylistEditor from '@/components/playlist/APlaylistEditor.vue' +import AUserSelector from '@/components/identities/AUserSelector.vue' const props = defineProps<{ show: Show @@ -455,13 +439,6 @@ const owners = useRelationList(showStore, () => props.show, 'ownerIds', userStor const { obj: playlist } = useObjectFromStore(() => props.show.defaultPlaylistId, playlistStore) const playlistId = useAPIObjectFieldCopy(showStore, show, 'defaultPlaylistId', { debounce: 0 }) -function searchUsers(query: string, signal: AbortSignal) { - return userStore.list({ - query: new URLSearchParams({ search: query }), - requestInit: { signal }, - }) -} - useBreadcrumbs(() => [ { title: t('navigation.shows'), route: { name: 'shows' } }, { title: props.show.name, route: { name: 'show', params: { showId: props.show.id.toString() } } }, diff --git a/src/components/UserPreview.vue b/src/components/identities/AUserPreview.vue similarity index 90% rename from src/components/UserPreview.vue rename to src/components/identities/AUserPreview.vue index 7b4b7cac77da6485aa11cff8d9153b6bb6de37b1..c9ad448dfcbca823f9e531d74ae10bb3e10a6e3d 100644 --- a/src/components/UserPreview.vue +++ b/src/components/identities/AUserPreview.vue @@ -4,11 +4,11 @@ :class="{ 'tw-bg-gray-100': !transparent }" > <span> - <span :aria-label="t('user.username')" class="tw-font-bold tw-block"> + <span :aria-label="t('user.fields.username')" class="tw-font-medium tw-block"> {{ user.username }} </span> <span class="empty:tw-hidden tw-opacity-80 tw-text-xs"> - <span v-if="name" class="tw-block" :aria-label="t('user.name')">{{ name }}</span> + <span v-if="name" class="tw-block" :aria-label="t('user.fields.name')">{{ name }}</span> <span v-if="user.email" class="tw-block">{{ user.email }}</span> </span> </span> diff --git a/src/components/identities/AUserSelector.vue b/src/components/identities/AUserSelector.vue new file mode 100644 index 0000000000000000000000000000000000000000..b392a64d829d74e35eef0464c3b6ba79599a7d41 --- /dev/null +++ b/src/components/identities/AUserSelector.vue @@ -0,0 +1,39 @@ +<template> + <ComboBoxSimple v-model="modelValue" :search-provider="searchUsers" v-bind="props"> + <template #default="{ choice, ...itemAttrs }"> + <li v-bind="itemAttrs"> + <AUserPreview :user="choice as SteeringUser" transparent /> + </li> + </template> + <template #selected="{ deselect, value }"> + <template + v-for="user in ensureArray(value as null | SteeringUser | SteeringUser[])" + :key="user.id" + > + <AUserPreview :user="user" :removable="!props.disabled" @remove="deselect(user)" /> + </template> + </template> + </ComboBoxSimple> +</template> + +<script setup lang="ts"> +import { SteeringUser, useUserStore } from '@/stores/auth' +import ComboBoxSimple, { ComboBoxSimpleProps } from '@/components/ComboBoxSimple.vue' +import AUserPreview from './AUserPreview.vue' + +const modelValue = defineModel<null | SteeringUser | SteeringUser[]>({ required: true }) +const props = defineProps<Omit<ComboBoxSimpleProps<SteeringUser>, 'choices' | 'searchProvider'>>() +const userStore = useUserStore() + +function searchUsers(query: string, signal: AbortSignal) { + return userStore.list({ + query: new URLSearchParams({ search: query }), + requestInit: { signal }, + }) +} + +function ensureArray(value: null | SteeringUser | SteeringUser[]) { + if (value === null) return [] + return Array.isArray(value) ? value : [value] +} +</script> diff --git a/src/i18n/de.js b/src/i18n/de.js index 975c8bdc29b0ca80e425b8f6800035c9f58675de..b1854cfab391941ac89c012aae189effef47c232 100644 --- a/src/i18n/de.js +++ b/src/i18n/de.js @@ -420,9 +420,11 @@ export default { }, user: { - username: 'Benutzername', - name: 'Name', - email: 'E-Mail', + fields: { + username: 'Benutzername', + name: 'Name', + email: 'E-Mail', + }, }, noteEditor: { diff --git a/src/i18n/en.js b/src/i18n/en.js index 921cb608f7156044f5355c683e7f968534a2372e..da3cec04a5fde7568ef5916a49ea265e7b03e710 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -421,9 +421,11 @@ export default { }, user: { - username: 'Username', - name: 'Name', - email: 'Email', + fields: { + username: 'Username', + name: 'Name', + email: 'Email', + }, }, noteEditor: {