mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-01 09:48:25 +00:00
Refactor AddPersons and related components for consistent state management and improved handling of selected suggestions.
- Replaced `search.selected` with a dedicated `selected` state and implemented explicit methods for adding, removing, and clearing selections. - Updated event handling and props (`addNewPersons`, `selected`, `updateSelected`, `cleanSelected`) for better separation of concerns and type safety. - Introduced `isSelected` utility for streamlined selection checks and replaced deprecated event usages. - Adjusted modal behaviors in `PersonChooseModal.vue` and `AddPersons.vue` for improved integration and alignment with new state logic.
This commit is contained in:
@@ -322,7 +322,7 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
addNewPersons({ selected, modal }) {
|
||||
addNewPersons({ selected }) {
|
||||
//console.log('@@@ CLICK button addNewPersons', selected);
|
||||
this.$store
|
||||
.dispatch("addRequestor", selected.shift())
|
||||
@@ -337,7 +337,6 @@ export default {
|
||||
});
|
||||
|
||||
this.$refs.addPersons.resetSearch(); // to cast child method
|
||||
modal.showModal = false;
|
||||
},
|
||||
saveFormOnTheFly(payload) {
|
||||
console.log(
|
||||
|
||||
@@ -10,16 +10,18 @@
|
||||
|
||||
<person-choose-modal
|
||||
v-if="showModalChoose"
|
||||
ref="personChooseModal"
|
||||
:modal-title="modalTitle"
|
||||
:options="options"
|
||||
:suggested="suggested"
|
||||
:selected="selected"
|
||||
:modal-dialog-class="'modal-dialog-scrollable modal-xl'"
|
||||
:allow-create="props.allowCreate"
|
||||
@close="closeModalChoose"
|
||||
@addNewPersons="(payload) => emit('addNewPersons', payload)"
|
||||
@onPickEntities="onPickEntities"
|
||||
@onAskForCreate="onAskForCreate"
|
||||
@triggerAddContact="triggerAddContact"
|
||||
@updateSelected="updateSelected"
|
||||
@cleanSelected="emptySelected"
|
||||
/>
|
||||
|
||||
<CreateModal
|
||||
@@ -48,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import {ref, computed, nextTick, useTemplateRef} from "vue";
|
||||
import PersonChooseModal from "./AddPersons/PersonChooseModal.vue";
|
||||
import type {
|
||||
Suggestion,
|
||||
@@ -64,7 +66,6 @@ import {Thirdparty, ThirdpartyCompany} from "../../../../../ChillThirdPartyBundl
|
||||
|
||||
interface AddPersonsConfig {
|
||||
suggested?: Suggestion[];
|
||||
selected?: Suggestion[];
|
||||
buttonTitle: string;
|
||||
modalTitle: string;
|
||||
options: SearchOptions;
|
||||
@@ -74,7 +75,6 @@ interface AddPersonsConfig {
|
||||
|
||||
const props = withDefaults(defineProps<AddPersonsConfig>(), {
|
||||
suggested: () => [],
|
||||
selected: () => [],
|
||||
allowCreate: () => true,
|
||||
types: () => ["person"],
|
||||
});
|
||||
@@ -85,11 +85,38 @@ const emit =
|
||||
}
|
||||
>();
|
||||
|
||||
const personChooseModal = useTemplateRef('personChooseModal');
|
||||
|
||||
/**
|
||||
* Flag to show/hide the modal "choose".
|
||||
*/
|
||||
const showModalChoose = ref(false);
|
||||
|
||||
/**
|
||||
* Flag to show/hide the modal "create".
|
||||
*/
|
||||
const showModalCreate = ref(false);
|
||||
|
||||
/**
|
||||
* Store the previous search query, stored while going from "search" state to "create"
|
||||
*/
|
||||
const query = ref("");
|
||||
|
||||
/**
|
||||
* Temporarily store the thirdparty company when calling "addContact"
|
||||
*/
|
||||
const thirdPartyParentAddContact = ref<ThirdpartyCompany|null>(null);
|
||||
|
||||
/**
|
||||
* Contains the selected elements.
|
||||
*
|
||||
* If the property option.uniq is true, this will contains only one element.
|
||||
*
|
||||
* Suggestion must be added/removed using the @link{addSuggestionToSelected} and @link{removeSuggestionFromSelected}
|
||||
* methods.
|
||||
*/
|
||||
const selected = ref<Suggestion[]>([]);
|
||||
|
||||
const getClassButton = computed(() => {
|
||||
const size = props.options?.button?.size ?? "";
|
||||
const type = props.options?.button?.type ?? "btn-create";
|
||||
@@ -113,55 +140,103 @@ const creatableEntityTypes = computed<CreatableEntityType[]>(() => {
|
||||
);
|
||||
});
|
||||
|
||||
function onAskForCreate(payload: { query: string }) {
|
||||
function onAskForCreate(payload: { query: string }): void {
|
||||
query.value = payload.query;
|
||||
closeModalChoose();
|
||||
showModalCreate.value = true;
|
||||
}
|
||||
|
||||
function openModalChoose() {
|
||||
function openModalChoose(): void {
|
||||
showModalChoose.value = true;
|
||||
}
|
||||
|
||||
function closeModalChoose() {
|
||||
function closeModalChoose(): void {
|
||||
showModalChoose.value = false;
|
||||
}
|
||||
|
||||
function closeModalCreate() {
|
||||
function closeModalCreate(): void {
|
||||
if (null !== thirdPartyParentAddContact) {
|
||||
thirdPartyParentAddContact.value = null;
|
||||
}
|
||||
showModalCreate.value = false;
|
||||
}
|
||||
|
||||
function triggerAddContact({parent}: {parent: ThirdpartyCompany}) {
|
||||
console.log("triggerAddContact", parent);
|
||||
/**
|
||||
* Called by PersonSuggestion's updateSelection event, when an element is checked/unchecked
|
||||
*/
|
||||
function updateSelected(payload: {suggestion: Suggestion, isSelected: boolean}): void {
|
||||
if (payload.isSelected) {
|
||||
addSuggestionToSelected(payload.suggestion);
|
||||
} else {
|
||||
removeSuggestionFromSelected(payload.suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
function addSuggestionToSelected(suggestion: Suggestion): void {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function removeSuggestionFromSelected(suggestion: Suggestion): void {
|
||||
selected.value = selected.value.filter((s: Suggestion) => s.key !== suggestion.key && s.result.id !== suggestion.result.id);
|
||||
}
|
||||
|
||||
function emptySelected(): void {
|
||||
selected.value.splice(0, selected.value.length);
|
||||
}
|
||||
|
||||
function onPickEntities(): void {
|
||||
emit("addNewPersons", { selected: selected.value });
|
||||
closeModalChoose();
|
||||
}
|
||||
|
||||
function triggerAddContact({parent}: {parent: ThirdpartyCompany}): void {
|
||||
closeModalChoose();
|
||||
openModalChoose();
|
||||
thirdPartyParentAddContact.value = parent;
|
||||
showModalCreate.value = true;
|
||||
}
|
||||
|
||||
function onPersonCreated(payload: { person: Person }) {
|
||||
console.log("onPersonCreated", payload);
|
||||
function onPersonCreated(payload: { person: Person }): void {
|
||||
showModalCreate.value = false;
|
||||
const suggestion = {
|
||||
result: payload.person,
|
||||
relevance: 999999,
|
||||
key: "person",
|
||||
};
|
||||
emit("addNewPersons", { selected: [suggestion] });
|
||||
addSuggestionToSelected(suggestion);
|
||||
if (props.options.uniq) {
|
||||
emit("addNewPersons", { selected: [suggestion] });
|
||||
} else {
|
||||
openModalChoose();
|
||||
}
|
||||
}
|
||||
|
||||
function onThirdPartyCreated(payload: {thirdParty: Thirdparty}) {
|
||||
console.log("onThirdPartyCreated", payload);
|
||||
function onThirdPartyCreated(payload: {thirdParty: Thirdparty}): void {
|
||||
showModalCreate.value = false;
|
||||
const suggestion = {
|
||||
result: payload.thirdParty,
|
||||
relevance: 999999,
|
||||
key: "thirdparty",
|
||||
};
|
||||
emit("addNewPersons", { selected: [suggestion] });
|
||||
addSuggestionToSelected(suggestion);
|
||||
if (props.options.uniq) {
|
||||
emit("addNewPersons", { selected: [suggestion] });
|
||||
} else {
|
||||
openModalChoose();
|
||||
}
|
||||
}
|
||||
|
||||
function resetSearch(): void {
|
||||
selected.value = [];
|
||||
personChooseModal.value?.resetSearch();
|
||||
}
|
||||
|
||||
defineExpose({resetSearch})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -62,10 +62,10 @@
|
||||
v-for="item in selectedAndSuggested.slice().reverse()"
|
||||
:key="itemKey(item)"
|
||||
:item="item"
|
||||
:search="search"
|
||||
:isSelected="isSelected(item)"
|
||||
:type="checkUniq"
|
||||
@update-selected="updateSelected"
|
||||
@trigger-add-contact="(payload) => emit('triggerAddContact', payload)"
|
||||
@update-selected="(payload) => emit('updateSelected', payload)"
|
||||
@trigger-add-contact="triggerAddContact"
|
||||
/>
|
||||
|
||||
<div
|
||||
@@ -124,29 +124,27 @@ interface Props {
|
||||
modalTitle: string;
|
||||
options: SearchOptions;
|
||||
suggested?: Suggestion[];
|
||||
selected?: Suggestion[];
|
||||
selected: Suggestion[];
|
||||
modalDialogClass?: string;
|
||||
allowCreate?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
suggested: () => [],
|
||||
selected: () => [],
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
allowCreate: () => true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
/** @deprecated use 'onPickEntities' */
|
||||
(e: "addNewPersons", payload: { selected: Suggestion[] }): void;
|
||||
(e: "onPickEntities", payload: { selected: EntitiesOrMe[] }): void;
|
||||
(e: "onPickEntities"): void;
|
||||
(e: "onAskForCreate", payload: { query: string }): void;
|
||||
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }): void;
|
||||
(e: "updateSelected", payload: {suggestion: Suggestion, isSelected: boolean}): void;
|
||||
(e: "cleanSelected"): void;
|
||||
}>();
|
||||
|
||||
const searchRef = ref<HTMLInputElement | null>(null);
|
||||
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
// give the focus on the search bar
|
||||
@@ -162,33 +160,16 @@ const search = reactive({
|
||||
priorSuggestion: {} as Partial<Suggestion>,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.selected,
|
||||
(newSelected) => {
|
||||
search.selected = newSelected ? [...newSelected] : [];
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.suggested,
|
||||
(newSuggested) => {
|
||||
search.suggested = newSuggested ? [...newSuggested] : [];
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const query = computed({
|
||||
get: () => search.query,
|
||||
set: (val: string) => setQuery(val),
|
||||
});
|
||||
const queryLength = computed(() => search.query.length);
|
||||
const suggestedCounter = computed(() => search.suggested.length);
|
||||
const selectedComputed = computed<Suggestion[]>(() => search.selected);
|
||||
const selectedCounter = computed(() => search.selected.length);
|
||||
|
||||
const checkUniq = computed(() =>
|
||||
props.options.uniq === true ? "radio" : "checkbox",
|
||||
props.options.uniq ? "radio" : "checkbox",
|
||||
);
|
||||
|
||||
const priorSuggestion = computed(() => search.priorSuggestion);
|
||||
@@ -203,6 +184,13 @@ function addPriorSuggestion() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 selectedAndSuggested = computed(() => {
|
||||
addPriorSuggestion();
|
||||
const uniqBy = (a: Suggestion[], key: (item: Suggestion) => string) => [
|
||||
@@ -260,17 +248,13 @@ function loadSuggestions(suggestedArr: Suggestion[]) {
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelected(value: Suggestion[]) {
|
||||
search.selected = value;
|
||||
}
|
||||
|
||||
function resetSuggestion() {
|
||||
search.query = "";
|
||||
search.suggested = [];
|
||||
}
|
||||
|
||||
function resetSelection() {
|
||||
search.selected = [];
|
||||
emit("cleanSelected");
|
||||
}
|
||||
|
||||
function resetSearch() {
|
||||
@@ -279,8 +263,8 @@ function resetSearch() {
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
search.suggested.forEach((item) => {
|
||||
search.selected.push(item);
|
||||
search.suggested.forEach((suggestion: Suggestion) => {
|
||||
emit("updateSelected", {suggestion, isSelected: true})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -292,10 +276,7 @@ function triggerAddContact(payload: {parent: ThirdpartyCompany}) {
|
||||
* Triggered when the user clicks on the "add" button.
|
||||
*/
|
||||
function pickEntities(): void {
|
||||
emit("addNewPersons", { selected: search.selected });
|
||||
emit("onPickEntities", {
|
||||
selected: search.selected.map((s: Suggestion) => s.result),
|
||||
});
|
||||
emit("onPickEntities", );
|
||||
search.query = "";
|
||||
emit("close");
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
<div>
|
||||
<input
|
||||
:type="type"
|
||||
v-model="selected"
|
||||
name="item"
|
||||
:id="item.key"
|
||||
:value="setValueByType(item, type)"
|
||||
v-model="isChecked"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -62,23 +61,18 @@ import {ThirdpartyCompany} from "../../../../../../ChillThirdPartyBundle/Resourc
|
||||
|
||||
const props = defineProps<{
|
||||
item: Suggestion;
|
||||
search: { selected: Suggestion[] };
|
||||
type: string;
|
||||
isSelected: boolean;
|
||||
type: "radio"|"checkbox";
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: "updateSelected", payload: Suggestion[]): void;
|
||||
(e: "updateSelected", payload: {suggestion: Suggestion, isSelected: boolean}): void;
|
||||
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }): void;
|
||||
}>();
|
||||
|
||||
// v-model for selected
|
||||
const selected = computed({
|
||||
get: () => props.search.selected,
|
||||
set: (value) => emit("updateSelected", value),
|
||||
});
|
||||
|
||||
const isChecked = computed(
|
||||
() => props.search.selected.indexOf(props.item) !== -1,
|
||||
);
|
||||
const isChecked = computed<boolean>({
|
||||
get: () => props.isSelected,
|
||||
set: value => emit("updateSelected", {suggestion: props.item, isSelected: props.type === "radio" ? true : value})
|
||||
})
|
||||
|
||||
function setValueByType(value: Suggestion, type: string) {
|
||||
return type === "radio" ? [value] : value;
|
||||
|
||||
Reference in New Issue
Block a user