518 lines
20 KiB
Vue

<template>
<a
class="btn"
:class="getClassButton"
:title="$t(buttonTitle || '')"
@click="openModal"
>
<span v-if="displayTextButton">{{ $t(buttonTitle || "") }}</span>
</a>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h3 class="modal-title">
{{ $t(modalTitle) }}
</h3>
</template>
<template #body-head>
<div class="modal-body">
<div class="search">
<label class="col-form-label" style="float: right">
{{
$tc(
"add_persons.suggested_counter",
suggestedCounter,
)
}}
</label>
<input
id="search-persons"
name="query"
v-model="query"
:placeholder="$t('add_persons.search_some_persons')"
ref="search"
/>
<i class="fa fa-search fa-lg" />
<i
class="fa fa-times"
v-if="queryLength >= 3"
@click="resetSuggestion"
/>
</div>
</div>
<div class="modal-body" v-if="checkUniq === 'checkbox'">
<div class="count">
<span>
<a v-if="suggestedCounter > 2" @click="selectAll">
{{ $t("action.check_all") }}
</a>
<a
v-if="selectedCounter > 0"
@click="resetSelection"
>
<i v-if="suggestedCounter > 2"> </i>
{{ $t("action.reset") }}
</a>
</span>
<span v-if="selectedCounter > 0">
{{
$tc(
"add_persons.selected_counter",
selectedCounter,
)
}}
</span>
</div>
</div>
</template>
<template #body>
<div class="results">
<person-suggestion
v-for="item in this.selectedAndSuggested
.slice()
.reverse()"
:key="itemKey(item)"
:item="item"
:search="search"
:type="checkUniq"
@save-form-on-the-fly="saveFormOnTheFly"
@new-prior-suggestion="newPriorSuggestion"
@update-selected="updateSelected"
/>
<div class="create-button">
<on-the-fly
v-if="
queryLength >= 3 &&
(options.type.includes('person') ||
options.type.includes('thirdparty'))
"
:button-text="
$t('onthefly.create.button', { q: query })
"
:allowed-types="options.type"
:query="query"
action="create"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-create"
@click.prevent="$emit('addNewPersons', { selected, modal })"
>
{{ $t("action.add") }}
</button>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import PersonSuggestion from "./AddPersons/PersonSuggestion";
import { searchEntities } from "ChillPersonAssets/vuejs/_api/AddPersons";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
export default {
name: "AddPersons",
components: {
Modal,
PersonSuggestion,
OnTheFly,
},
props: ["buttonTitle", "modalTitle", "options"],
emits: ["addNewPersons"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
search: {
query: "",
previousQuery: "",
currentSearchQueryController: null,
suggested: [],
selected: [],
priorSuggestion: {},
},
};
},
computed: {
query: {
set(query) {
return this.setQuery(query);
},
get() {
return this.search.query;
},
},
queryLength() {
return this.search.query.length;
},
suggested() {
return this.search.suggested;
},
suggestedCounter() {
return this.search.suggested.length;
},
selected() {
return this.search.selected;
},
selectedCounter() {
return this.search.selected.length;
},
selectedAndSuggested() {
this.addPriorSuggestion();
const uniqBy = (a, key) => [
...new Map(a.map((x) => [key(x), x])).values(),
];
let union = [
...new Set([
...this.suggested.slice().reverse(),
...this.selected.slice().reverse(),
]),
];
return uniqBy(union, (k) => k.key);
},
getClassButton() {
let size =
typeof this.options.button !== "undefined" &&
typeof this.options.button.size !== "undefined"
? this.options.button.size
: "";
let type =
typeof this.options.button !== "undefined" &&
typeof this.options.button.type !== "undefined"
? this.options.button.type
: "btn-create";
return size ? size + " " + type : type;
},
displayTextButton() {
return typeof this.options.button !== "undefined" &&
typeof this.options.button.display !== "undefined"
? this.options.button.display
: true;
},
checkUniq() {
if (this.options.uniq === true) {
return "radio";
}
return "checkbox";
},
priorSuggestion() {
return this.search.priorSuggestion;
},
hasPriorSuggestion() {
return this.search.priorSuggestion.key ? true : false;
},
},
methods: {
openModal() {
this.modal.showModal = true;
this.$nextTick(function () {
this.$refs.search.focus();
});
},
setQuery(query) {
this.search.query = query;
setTimeout(
function () {
if (query === "") {
this.loadSuggestions([]);
return;
}
if (query === this.search.query) {
if (this.search.currentSearchQueryController !== null) {
this.search.currentSearchQueryController.abort();
}
this.search.currentSearchQueryController =
new AbortController();
searchEntities(
{ query, options: this.options },
this.search.currentSearchQueryController.signal,
)
.then(
(suggested) =>
new Promise((resolve) => {
this.loadSuggestions(suggested.results);
resolve();
}),
)
.catch((error) => {
if (error instanceof DOMException) {
if (error.name === "AbortError") {
console.log(
"request aborted due to user continue typing",
);
return;
}
}
throw error;
});
}
}.bind(this),
query.length > 3 ? 300 : 700,
);
},
loadSuggestions(suggested) {
this.search.suggested = suggested;
this.search.suggested.forEach(function (item) {
item.key = this.itemKey(item);
}, this);
},
updateSelected(value) {
//console.log('value', value);
this.search.selected = value;
},
resetSearch() {
this.resetSelection();
this.resetSuggestion();
},
resetSuggestion() {
this.search.query = "";
this.search.suggested = [];
},
resetSelection() {
this.search.selected = [];
},
selectAll() {
this.search.suggested.forEach(function (item) {
this.search.selected.push(item);
}, this);
},
itemKey(item) {
return item.result.type + item.result.id;
},
addPriorSuggestion() {
// console.log('prior suggestion', this.priorSuggestion);
if (this.hasPriorSuggestion) {
// console.log('addPriorSuggestion',);
this.suggested.unshift(this.priorSuggestion);
this.selected.unshift(this.priorSuggestion);
this.newPriorSuggestion(null);
}
},
newPriorSuggestion(entity) {
// console.log('newPriorSuggestion', entity);
if (entity !== null) {
let suggestion = {
key: entity.type + entity.id,
relevance: 0.5,
result: entity,
};
this.search.priorSuggestion = suggestion;
// console.log('search priorSuggestion', this.search.priorSuggestion);
this.addPriorSuggestion(suggestion);
} else {
this.search.priorSuggestion = {};
}
},
saveFormOnTheFly({ type, data }) {
console.log(
"saveFormOnTheFly from addPersons, type",
type,
", data",
data,
);
if (type === "person") {
makeFetch("POST", "/api/1.0/person/person.json", data)
.then((responsePerson) => {
this.newPriorSuggestion(responsePerson);
this.$refs.onTheFly.closeModal();
if (null !== data.addressId) {
const household = {
type: "household",
};
const address = {
id: data.addressId,
};
makeFetch(
"POST",
"/api/1.0/person/household.json",
household,
)
.then((responseHousehold) => {
const member = {
concerned: [
{
person: {
type: "person",
id: responsePerson.id,
},
start_date: {
// TODO: use date.ts methods (low priority)
datetime: `${new Date().toISOString().split("T")[0]}T00:00:00+02:00`,
},
holder: false,
comment: null,
},
],
destination: {
type: "household",
id: responseHousehold.id,
},
composition: null,
};
return makeFetch(
"POST",
"/api/1.0/person/household/members/move.json",
member,
)
.then((_response) => {
makeFetch(
"POST",
`/api/1.0/person/household/${responseHousehold.id}/address.json`,
address,
)
.then((_response) => {
console.log(_response);
})
.catch((error) => {
if (
error.name ===
"ValidationException"
) {
for (let v of error.violations) {
this.$toast.open({
message: v,
});
}
} else {
this.$toast.open({
message:
"An error occurred",
});
}
});
})
.catch((error) => {
if (
error.name ===
"ValidationException"
) {
for (let v of error.violations) {
this.$toast.open({
message: v,
});
}
} else {
this.$toast.open({
message:
"An error occurred",
});
}
});
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({
message: "An error occurred",
});
}
});
}
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} else if (type === "thirdparty") {
makeFetch("POST", "/api/1.0/thirdparty/thirdparty.json", data)
.then((response) => {
this.newPriorSuggestion(response);
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}
},
},
};
</script>
<style lang="scss">
li.add-persons {
a {
cursor: pointer;
}
}
div.body-head {
overflow-y: unset;
div.modal-body:first-child {
margin: auto 4em;
div.search {
position: relative;
input {
width: 100%;
padding: 1.2em 1.5em 1.2em 2.5em;
//margin: 1em 0;
}
i {
position: absolute;
opacity: 0.5;
padding: 0.65em 0;
top: 50%;
}
i.fa-search {
left: 0.5em;
}
i.fa-times {
right: 1em;
padding: 0.75em 0;
cursor: pointer;
}
}
}
div.modal-body:last-child {
padding-bottom: 0;
}
div.count {
margin: -0.5em 0 0.7em;
display: flex;
justify-content: space-between;
a {
cursor: pointer;
}
}
}
.create-button > a {
margin-top: 0.5em;
margin-left: 2.6em;
}
</style>