diff --git a/src/components/model/ModelForm.svelte b/src/components/model/ModelForm.svelte index 0a2b59b1da..f036096c68 100644 --- a/src/components/model/ModelForm.svelte +++ b/src/components/model/ModelForm.svelte @@ -9,6 +9,7 @@ import { getShortISOForDate } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import Input from '../form/Input.svelte'; + import UserInput from '../ui/Tags/UserInput.svelte'; export let initialModelDescription: string = ''; export let initialModelName: string = ''; @@ -17,6 +18,7 @@ export let modelId: number | undefined; export let createdAt: string | undefined; export let user: User | null; + export let users: UserId[] = []; const dispatch = createEventDispatcher<{ createPlan: number; @@ -69,6 +71,14 @@ dispatch('deleteModel'); } + function onSetOwner(event: CustomEvent) { + owner = event.detail[0]; + } + + function onClearOwner() { + owner = null; + } + onDestroy(() => { resetModelStores(); }); @@ -120,14 +130,22 @@ - {#if createdAt} diff --git a/src/components/ui/Tags/PlanCollaboratorInput.svelte b/src/components/ui/Tags/PlanCollaboratorInput.svelte index 0ee8ef7615..9e6a8ed5f3 100644 --- a/src/components/ui/Tags/PlanCollaboratorInput.svelte +++ b/src/components/ui/Tags/PlanCollaboratorInput.svelte @@ -4,10 +4,8 @@ import { createEventDispatcher } from 'svelte'; import type { User, UserId } from '../../../types/app'; import type { Plan, PlanCollaborator, PlanCollaboratorSlim, PlanSlimmer } from '../../../types/plan'; - import type { PlanCollaboratorTag, Tag, TagsChangeEvent } from '../../../types/tags'; import type { ActionArray } from '../../../utilities/useActions'; - import PlanCollaboratorInputRow from './PlanCollaboratorInputRow.svelte'; - import TagsInput from './TagsInput.svelte'; + import UserInput from './UserInput.svelte'; export let collaborators: PlanCollaboratorSlim[] = []; export let users: UserId[] = []; @@ -21,148 +19,66 @@ delete: string; }>(); - let inputRef: TagsInput | null; - let options: PlanCollaboratorTag[] = []; - let selected: Tag[] = []; + let groups: { + name: string; + users: UserId[]; + }[] = []; $: allowableCollaborators = users.filter(user => { return !collaborators.find(collaborator => collaborator.collaborator === user); }); - $: userIsCollaborator = (userId: UserId) => { - return allowableCollaborators.indexOf(userId) < 0; - }; - $: if (users) { - let newOptions: PlanCollaboratorTag[] = users.map(userToTag); - plans.forEach(p => { - // Filter out current plan - if (p.id !== plan.id) { - newOptions.push(planToTag(p)); - } - }); - // Filter out plans where owner and collaborators are already added - newOptions = newOptions.filter(tag => { - // If the tag is a user tag we don't need to filter on it - if (!tag.plan) { - return true; - } - // If owner is already a collaborator and there are no tag plan collaborators - // then this tag can be skipped - const ownerIsCollaborator = userIsCollaborator(tag.plan.owner); - if (ownerIsCollaborator && tag.plan.collaborators.length < 1) { - return false; - } - let anyTagCollaboratorsNotCollaborators = !!tag.plan.collaborators.find( - collaborator => !userIsCollaborator(collaborator.collaborator), - ); - return !ownerIsCollaborator || anyTagCollaboratorsNotCollaborators; - }); - - options = newOptions.sort((optionA, optionB) => { - if (optionA.plan && !optionB.plan) { - return -1; - } - if (!optionA.plan && optionB.plan) { - return 1; - } - if (optionA.plan && optionB.plan) { - return optionA.plan.updated_at > optionB.plan.updated_at ? -1 : 1; - } - return optionA.name < optionB.name ? -1 : 0; - }); - } - - $: selected = collaborators.map(collaborator => userToTag(collaborator.collaborator)); + let newGroupOptions: { + name: string; + users: UserId[]; + }[] = []; + + [...plans] + .sort((planA, planB) => { + return planA.updated_at > planB.updated_at ? -1 : 1; + }) + .forEach(p => { + // Filter out current plan + if (p.id !== plan.id) { + newGroupOptions.push({ + name: p.name, + users: [p.owner, ...p.collaborators.map(({ collaborator }) => `${collaborator}`)], + }); + } + }); - function getTagName(tag: PlanCollaboratorTag): string { - return tag.plan ? tag.plan.name : tag.name; + groups = newGroupOptions; } - function compareTags(tagA: PlanCollaboratorTag, tagB: PlanCollaboratorTag): boolean { - return getTagName(tagA) === getTagName(tagB); - } + function addTag(event: CustomEvent) { + const { detail: newUsers } = event; + const newCollaborators: PlanCollaborator[] = []; - function addTag(tag: PlanCollaboratorTag) { - inputRef?.closeSuggestions(); - inputRef?.updatePopperPosition(); - let newCollaborators: PlanCollaborator[] = []; - const tagPlan = tag.plan; - if (!tagPlan) { - // Username case - newCollaborators.push({ collaborator: tag.name, plan_id: plan.id }); - } else { - // Add collaborators from plan if not already a collaborator on the target plan - const tagPlanCollaborators = tagPlan.collaborators.map(c => c.collaborator); - tagPlanCollaborators.forEach(userId => { - if (!userIsCollaborator(userId)) { - newCollaborators.push({ collaborator: userId, plan_id: plan.id }); - } - }); + newUsers.forEach(user => { + newCollaborators.push({ collaborator: user, plan_id: plan.id }); + }); - // Add plan owner if not already a collaborator in target/source plans - if (!userIsCollaborator(tagPlan.owner) && tagPlanCollaborators.indexOf(tagPlan.owner) < 0) { - newCollaborators.push({ collaborator: tagPlan.owner, plan_id: plan.id }); - } - } if (user) { dispatch('create', newCollaborators); allowableCollaborators = allowableCollaborators.filter( collaborator => !newCollaborators.find(c => c.collaborator === collaborator), ); - selected = selected.concat(newCollaborators.map(c => userToTag(c.collaborator))); - } - } - - function userToTag(userId: UserId): PlanCollaboratorTag { - return { - color: '#EBECEC', - created_at: '', - id: -1, - name: userId || 'Unk', - owner: '', - }; - } - - function planToTag(plan: PlanSlimmer): PlanCollaboratorTag { - return { - color: '#EBECEC', - created_at: '', - id: -1, - name: '', - owner: '', - plan, - }; - } - - async function onTagsInputChange(event: TagsChangeEvent) { - const { - detail: { tag, type }, - } = event; - if (type === 'remove') { - dispatch('delete', tag.name); } } - collaborator)} tagDisplayName="collaborator" - {compareTags} - {getTagName} - {options} - {selected} - minWidth={168} - on:change={onTagsInputChange} - let:prop={tag} + userGroups={groups} + users={allowableCollaborators} {use} -> - - + {user} + on:create={addTag} + on:delete +/> diff --git a/src/components/ui/Tags/TagsInput.svelte b/src/components/ui/Tags/TagsInput.svelte index 9403255ad2..fbc4604de0 100644 --- a/src/components/ui/Tags/TagsInput.svelte +++ b/src/components/ui/Tags/TagsInput.svelte @@ -19,6 +19,7 @@ updatePopperPosition(); closeSuggestions(); }; + export let allowMultiple: boolean = true; export let createTagObject: (name: string) => Tag = (name: string) => { return { color: generateRandomPastelColor(), created_at: '', id: -1, name, owner: '' }; }; @@ -239,19 +240,21 @@ onTagRemove(tag)} {disabled} ariaRole="option" /> {/each} {#if !disabled || (disabled && !selectedTags.length)} - + {#if !(!allowMultiple && selectedTags.length)} + + {/if} {/if} {#if suggestionsVisible} @@ -311,6 +314,7 @@ display: flex; gap: 8px; max-height: 40vh; + overflow: hidden; padding: 2px; } diff --git a/src/components/ui/Tags/UserInput.svelte b/src/components/ui/Tags/UserInput.svelte new file mode 100644 index 0000000000..d439b91c67 --- /dev/null +++ b/src/components/ui/Tags/UserInput.svelte @@ -0,0 +1,153 @@ + + + + + + + + + diff --git a/src/components/ui/Tags/PlanCollaboratorInputRow.svelte b/src/components/ui/Tags/UserInputRow.svelte similarity index 54% rename from src/components/ui/Tags/PlanCollaboratorInputRow.svelte rename to src/components/ui/Tags/UserInputRow.svelte index 7e437e9d75..da60b007e5 100644 --- a/src/components/ui/Tags/PlanCollaboratorInputRow.svelte +++ b/src/components/ui/Tags/UserInputRow.svelte @@ -3,31 +3,37 @@ -{#if tag.plan} -
-
- {tag.plan.name} +{#if tagGroup} +
+
+ {tagGroup.name}
-
- {#if planUsers.length > 0} -
- +
+ {#if users.length > 0} +
+
@@ -45,20 +51,21 @@ {/if}