mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-12 23:27:39 +00:00
Refactor PersonSuggestion and PersonChooseModal for streamlined state management and improved prop handling.
- Replaced `search.suggested` and `search.selected` with dedicated reactive `suggested` and `selected` states. - Updated `PersonSuggestion.vue` to handle selection via `onUpdateValue` and removed `v-model` usage. - Simplified `PersonChooseModal.vue` to compute `selectedAndSuggested` directly using props. - Adjusted `types.ts` to refine `Search.results` and standardize `Suggestion` structure.
This commit is contained in:
@@ -429,7 +429,6 @@ export interface Suggestion {
|
||||
relevance: number;
|
||||
result: Entities;
|
||||
}
|
||||
|
||||
export interface SearchPagination {
|
||||
first: number;
|
||||
items_per_page: number;
|
||||
@@ -441,7 +440,7 @@ export interface SearchPagination {
|
||||
export interface Search {
|
||||
count: number;
|
||||
pagination: SearchPagination;
|
||||
results: Suggestion[];
|
||||
results: {relevance: number, result: Entities}[];
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
|
||||
@@ -85,7 +85,8 @@ const emit =
|
||||
}
|
||||
>();
|
||||
|
||||
const personChooseModal = useTemplateRef('personChooseModal');
|
||||
type PersonChooseModalType = InstanceType<typeof PersonChooseModal>;
|
||||
const personChooseModal = useTemplateRef<PersonChooseModalType>('personChooseModal');
|
||||
|
||||
/**
|
||||
* Flag to show/hide the modal "choose".
|
||||
@@ -173,12 +174,15 @@ function updateSelected(payload: {suggestion: Suggestion, isSelected: boolean}):
|
||||
}
|
||||
|
||||
function addSuggestionToSelected(suggestion: Suggestion): void {
|
||||
console.log("addSuggestionToSelected", suggestion);
|
||||
console.log("before", selected.value);
|
||||
if (props.options.uniq) {
|
||||
selected.value = [suggestion];
|
||||
} else {
|
||||
// replace by an array with unique values
|
||||
selected.value = [...selected.value, suggestion].filter((value, index, array) => array.indexOf(value) === index);
|
||||
}
|
||||
console.log("after", selected.value);
|
||||
}
|
||||
|
||||
function removeSuggestionFromSelected(suggestion: Suggestion): void {
|
||||
|
||||
@@ -59,10 +59,10 @@
|
||||
<template #body>
|
||||
<div class="results">
|
||||
<person-suggestion
|
||||
v-for="item in selectedAndSuggested.slice().reverse()"
|
||||
:key="itemKey(item)"
|
||||
v-for="item in selectedAndSuggested"
|
||||
:key="item.key"
|
||||
:item="item"
|
||||
:isSelected="isSelected(item)"
|
||||
:isSelected="item.isSelected"
|
||||
:type="checkUniq"
|
||||
@update-selected="(payload) => emit('updateSelected', payload)"
|
||||
@trigger-add-contact="triggerAddContact"
|
||||
@@ -116,7 +116,7 @@ import type {
|
||||
Search,
|
||||
EntityType,
|
||||
SearchOptions,
|
||||
EntitiesOrMe,
|
||||
EntitiesOrMe, Entities,
|
||||
} from "ChillPersonAssets/types";
|
||||
import {ThirdpartyCompany} from "../../../../../../ChillThirdPartyBundle/Resources/public/types";
|
||||
|
||||
@@ -155,54 +155,52 @@ const search = reactive({
|
||||
query: "" as string,
|
||||
previousQuery: "" as string,
|
||||
currentSearchQueryController: null as AbortController | null,
|
||||
suggested: (props.suggested ?? []) as Suggestion[],
|
||||
selected: (props.selected ?? []) as Suggestion[],
|
||||
priorSuggestion: {} as Partial<Suggestion>,
|
||||
});
|
||||
|
||||
/**
|
||||
* Contains the suggested entities from the search results.
|
||||
*
|
||||
* In other words, those entities are displayed and selectable by the user
|
||||
*/
|
||||
const suggested = ref<Suggestion[]>([]);
|
||||
|
||||
const query = computed({
|
||||
get: () => search.query,
|
||||
set: (val: string) => setQuery(val),
|
||||
});
|
||||
const queryLength = computed(() => search.query.length);
|
||||
const suggestedCounter = computed(() => search.suggested.length);
|
||||
const selectedCounter = computed(() => search.selected.length);
|
||||
const suggestedCounter = computed(() => suggested.value.length);
|
||||
const selectedCounter = computed(() => props.selected.length);
|
||||
|
||||
const checkUniq = computed(() =>
|
||||
props.options.uniq ? "radio" : "checkbox",
|
||||
);
|
||||
|
||||
const priorSuggestion = computed(() => search.priorSuggestion);
|
||||
const hasPriorSuggestion = computed(() => !!search.priorSuggestion.key);
|
||||
|
||||
const itemKey = (item: Suggestion) => item.result.type + item.result.id;
|
||||
|
||||
function addPriorSuggestion() {
|
||||
if (hasPriorSuggestion.value) {
|
||||
search.suggested.unshift(priorSuggestion.value as Suggestion);
|
||||
search.selected.unshift(priorSuggestion.value as Suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the suggestion is present in the selected
|
||||
* Return true if the suggestion is present in the selected.
|
||||
*/
|
||||
function isSelected(item: Suggestion): boolean {
|
||||
return search.selected.some((s) => s.key === item.key);
|
||||
const r = props.selected.some((s: Suggestion) => s.key === item.key);
|
||||
console.log('isSelected', item.key, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
const selectedAndSuggested = computed(() => {
|
||||
addPriorSuggestion();
|
||||
const uniqBy = (a: Suggestion[], key: (item: Suggestion) => string) => [
|
||||
...new Map(a.map((x) => [key(x), x])).values(),
|
||||
];
|
||||
const union = [
|
||||
...new Set([
|
||||
...search.suggested.slice().reverse(),
|
||||
...search.selected.slice().reverse(),
|
||||
]),
|
||||
];
|
||||
return uniqBy(union, (k: Suggestion) => k.key);
|
||||
const selectedAndSuggested = computed<(Suggestion & {isSelected: boolean})[]>(() => {
|
||||
const selectedAndSuggested = [];
|
||||
|
||||
// add selected that are not in the search results
|
||||
for (const selected of props.selected) {
|
||||
if (!suggested.value.some((s: Suggestion) => s.key === selected.key)) {
|
||||
selectedAndSuggested.push({...selected, isSelected: false});
|
||||
}
|
||||
}
|
||||
for (const suggestion of suggested.value) {
|
||||
selectedAndSuggested.push({...suggestion, isSelected: props.selected.some((s: Suggestion) => s.key === suggestion.key)});
|
||||
}
|
||||
|
||||
|
||||
return selectedAndSuggested;
|
||||
});
|
||||
|
||||
function setQuery(q: string) {
|
||||
@@ -241,16 +239,20 @@ function setQuery(q: string) {
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function loadSuggestions(suggestedArr: Suggestion[]) {
|
||||
search.suggested = suggestedArr;
|
||||
search.suggested.forEach((item) => {
|
||||
item.key = itemKey(item);
|
||||
|
||||
function loadSuggestions(suggestedArr: {relevance: number, result: Entities}[]): void {
|
||||
suggested.value = suggestedArr.map((item) => {
|
||||
return {
|
||||
key: item.result.type + item.result.id,
|
||||
relevance: item.relevance,
|
||||
result: item.result
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetSuggestion() {
|
||||
search.query = "";
|
||||
search.suggested = [];
|
||||
suggested.value = [];
|
||||
}
|
||||
|
||||
function resetSelection() {
|
||||
@@ -263,7 +265,7 @@ function resetSearch() {
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
search.suggested.forEach((suggestion: Suggestion) => {
|
||||
suggested.value.forEach((suggestion: Suggestion) => {
|
||||
emit("updateSelected", {suggestion, isSelected: true})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<template>
|
||||
<div class="list-item" :class="{ checked: isChecked }">
|
||||
<label>
|
||||
<div>
|
||||
<input
|
||||
:type="type"
|
||||
:value="item.key"
|
||||
name="item"
|
||||
:id="item.key"
|
||||
v-model="isChecked"
|
||||
@click="onUpdateValue"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<suggestion-person
|
||||
v-if="isSuggestionForPerson(item)"
|
||||
@@ -69,13 +68,18 @@ const emit = defineEmits<{
|
||||
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }): void;
|
||||
}>();
|
||||
|
||||
const isChecked = computed<boolean>({
|
||||
get: () => props.isSelected,
|
||||
set: value => emit("updateSelected", {suggestion: props.item, isSelected: props.type === "radio" ? true : value})
|
||||
})
|
||||
const isChecked = computed<boolean>(() => props.isSelected)
|
||||
|
||||
function setValueByType(value: Suggestion, type: string) {
|
||||
return type === "radio" ? [value] : value;
|
||||
const onUpdateValue = (event: Event) => {
|
||||
const target = event?.target;
|
||||
if (!(target instanceof HTMLInputElement)) {
|
||||
console.error("the value of checked is not an HTMLInputElement");
|
||||
return;
|
||||
}
|
||||
console.log("onUpdateValue", target);
|
||||
console.log("is selected", props.type === "radio" ? true: target.checked);
|
||||
|
||||
emit("updateSelected", {suggestion: props.item, isSelected: props.type === "radio" ? true : target.checked});
|
||||
}
|
||||
|
||||
function triggerAddContact(payload: {parent: ThirdpartyCompany}) {
|
||||
|
||||
Reference in New Issue
Block a user