mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-01-14 21:31:23 +00:00
Merge branch 'master' into fork/boriswa/1849-1848-1920-1921-fix-bugs
# Conflicts: # src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/Location/NewLocation.vue
This commit is contained in:
@@ -27,7 +27,8 @@ class ByActivityNumberAggregator implements AggregatorInterface
|
||||
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
|
||||
{
|
||||
$qb
|
||||
->addSelect('(SELECT COUNT(activity.id) FROM '.Activity::class.' activity WHERE activity.accompanyingPeriod = acp) AS activity_by_number_aggregator')
|
||||
// Use a distinct alias inside the subquery to avoid colliding with the root alias "activity"
|
||||
->addSelect('(SELECT COUNT(agg_activity.id) FROM '.Activity::class.' agg_activity WHERE agg_activity.accompanyingPeriod = acp) AS activity_by_number_aggregator')
|
||||
->addGroupBy('activity_by_number_aggregator');
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ class ByActivityNumberAggregator implements AggregatorInterface
|
||||
{
|
||||
return static function ($value) {
|
||||
if ('_header' === $value) {
|
||||
return '';
|
||||
return 'Count activities linked to an accompanying period';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
|
||||
@@ -1,114 +1,123 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-sm btn-create" @click="openModal">
|
||||
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-sm btn-create" @click="openModal">
|
||||
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<teleport to="body">
|
||||
<modal
|
||||
v-if="modal.showModal"
|
||||
:modalDialogClass="modal.modalDialogClass"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template #header>
|
||||
<h3 class="modal-title">
|
||||
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
|
||||
</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<form>
|
||||
<div class="alert alert-warning" v-if="errors.length">
|
||||
<ul>
|
||||
<li v-for="(e, i) in errors" :key="i">
|
||||
{{ e }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<teleport to="body">
|
||||
<modal
|
||||
v-if="modal.showModal"
|
||||
:modalDialogClass="modal.modalDialogClass"
|
||||
@close="modal.showModal = false"
|
||||
>
|
||||
<template #header>
|
||||
<h3 class="modal-title">
|
||||
{{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
|
||||
</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<form>
|
||||
<div class="alert alert-warning" v-if="errors.length">
|
||||
<ul>
|
||||
<li v-for="(e, i) in errors" :key="i">
|
||||
{{ e }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<select
|
||||
class="form-select form-select-lg"
|
||||
id="type"
|
||||
required
|
||||
v-model="selectType"
|
||||
>
|
||||
<option selected disabled value="">
|
||||
{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
|
||||
</option>
|
||||
<option v-for="t in locationTypes" :value="t" :key="t.id">
|
||||
{{ localizeString(t.title) }}
|
||||
</option>
|
||||
</select>
|
||||
<label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<select
|
||||
class="form-select form-select-lg"
|
||||
id="type"
|
||||
required
|
||||
v-model="selectType"
|
||||
>
|
||||
<option selected disabled value="">
|
||||
{{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
|
||||
</option>
|
||||
<option
|
||||
v-for="t in locationTypes"
|
||||
:value="t"
|
||||
:key="t.id"
|
||||
>
|
||||
{{ localizeString(t.title) }}
|
||||
</option>
|
||||
</select>
|
||||
<label>{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_TYPE)
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="name"
|
||||
v-model="inputName"
|
||||
placeholder
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_NAME)
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="name"
|
||||
v-model="inputName"
|
||||
placeholder
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_NAME)
|
||||
}}</label>
|
||||
</div>
|
||||
|
||||
<add-address
|
||||
:context="addAddress.context"
|
||||
:options="addAddress.options"
|
||||
:addressChangedCallback="submitNewAddress"
|
||||
v-if="showAddAddress"
|
||||
ref="addAddress"
|
||||
/>
|
||||
<add-address
|
||||
:context="addAddress.context"
|
||||
:options="addAddress.options"
|
||||
:addressChangedCallback="submitNewAddress"
|
||||
v-if="showAddAddress"
|
||||
ref="addAddress"
|
||||
/>
|
||||
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber1"
|
||||
v-model="inputPhonenumber1"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber1">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="hasPhonenumber1">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber2"
|
||||
v-model="inputPhonenumber2"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber2">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="email"
|
||||
v-model="inputEmail"
|
||||
placeholder
|
||||
/>
|
||||
<label for="email">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
|
||||
}}</label>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button class="btn btn-save" @click.prevent="saveNewLocation">
|
||||
{{ trans(SAVE) }}
|
||||
</button>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber1"
|
||||
v-model="inputPhonenumber1"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber1">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="hasPhonenumber1">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="phonenumber2"
|
||||
v-model="inputPhonenumber2"
|
||||
placeholder
|
||||
/>
|
||||
<label for="phonenumber2">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="form-floating mb-3" v-if="showContactData">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="email"
|
||||
v-model="inputEmail"
|
||||
placeholder
|
||||
/>
|
||||
<label for="email">{{
|
||||
trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
|
||||
}}</label>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
<template #footer>
|
||||
<button
|
||||
class="btn btn-save"
|
||||
@click.prevent="saveNewLocation"
|
||||
>
|
||||
{{ trans(SAVE) }}
|
||||
</button>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -119,236 +128,241 @@ import { getLocationTypes } from "../../api";
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
||||
import {
|
||||
SAVE,
|
||||
ACTIVITY_LOCATION_FIELDS_EMAIL,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
|
||||
ACTIVITY_LOCATION_FIELDS_NAME,
|
||||
ACTIVITY_LOCATION_FIELDS_TYPE,
|
||||
ACTIVITY_CHOOSE_LOCATION_TYPE,
|
||||
ACTIVITY_CREATE_NEW_LOCATION,
|
||||
trans,
|
||||
SAVE,
|
||||
ACTIVITY_LOCATION_FIELDS_EMAIL,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
|
||||
ACTIVITY_LOCATION_FIELDS_NAME,
|
||||
ACTIVITY_LOCATION_FIELDS_TYPE,
|
||||
ACTIVITY_CHOOSE_LOCATION_TYPE,
|
||||
ACTIVITY_CREATE_NEW_LOCATION,
|
||||
ACTIVITY_EDIT_ADDRESS,
|
||||
ACTIVITY_CREATE_ADDRESS,
|
||||
trans,
|
||||
} from "translator";
|
||||
|
||||
export default {
|
||||
name: "NewLocation",
|
||||
components: {
|
||||
Modal,
|
||||
AddAddress,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
trans,
|
||||
SAVE,
|
||||
ACTIVITY_LOCATION_FIELDS_EMAIL,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
|
||||
ACTIVITY_LOCATION_FIELDS_NAME,
|
||||
ACTIVITY_LOCATION_FIELDS_TYPE,
|
||||
ACTIVITY_CHOOSE_LOCATION_TYPE,
|
||||
ACTIVITY_CREATE_NEW_LOCATION,
|
||||
};
|
||||
},
|
||||
props: ["availableLocations"],
|
||||
data() {
|
||||
return {
|
||||
errors: [],
|
||||
selected: {
|
||||
type: null,
|
||||
name: null,
|
||||
addressId: null,
|
||||
phonenumber1: null,
|
||||
phonenumber2: null,
|
||||
email: null,
|
||||
},
|
||||
locationTypes: [],
|
||||
modal: {
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
},
|
||||
addAddress: {
|
||||
options: {
|
||||
button: {
|
||||
text: {
|
||||
create: "activity.create_address",
|
||||
edit: "activity.edit_address",
|
||||
},
|
||||
size: "btn-sm",
|
||||
},
|
||||
title: {
|
||||
create: "activity.create_address",
|
||||
edit: "activity.edit_address",
|
||||
},
|
||||
},
|
||||
context: {
|
||||
target: {
|
||||
//name, id
|
||||
},
|
||||
edit: false,
|
||||
addressId: null,
|
||||
defaults: window.addaddress,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["activity"]),
|
||||
selectType: {
|
||||
get() {
|
||||
return this.selected.type;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.type = value;
|
||||
},
|
||||
name: "NewLocation",
|
||||
components: {
|
||||
Modal,
|
||||
AddAddress,
|
||||
},
|
||||
inputName: {
|
||||
get() {
|
||||
return this.selected.name;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.name = value;
|
||||
},
|
||||
},
|
||||
inputEmail: {
|
||||
get() {
|
||||
return this.selected.email;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.email = value;
|
||||
},
|
||||
},
|
||||
inputPhonenumber1: {
|
||||
get() {
|
||||
return this.selected.phonenumber1;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber1 = value;
|
||||
},
|
||||
},
|
||||
inputPhonenumber2: {
|
||||
get() {
|
||||
return this.selected.phonenumber2;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber2 = value;
|
||||
},
|
||||
},
|
||||
hasPhonenumber1() {
|
||||
return (
|
||||
this.selected.phonenumber1 !== null && this.selected.phonenumber1 !== ""
|
||||
);
|
||||
},
|
||||
showAddAddress() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.addressRequired !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
showContactData() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.contactData !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getLocationTypesList();
|
||||
},
|
||||
methods: {
|
||||
localizeString,
|
||||
checkForm() {
|
||||
let cond = true;
|
||||
this.errors = [];
|
||||
if (!this.selected.type) {
|
||||
this.errors.push("Type de localisation requis");
|
||||
cond = false;
|
||||
} else {
|
||||
if (
|
||||
this.selected.type.addressRequired === "required" &&
|
||||
!this.selected.addressId
|
||||
) {
|
||||
this.errors.push("Adresse requise");
|
||||
cond = false;
|
||||
}
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.phonenumber1
|
||||
) {
|
||||
this.errors.push("Numéro de téléphone requis");
|
||||
cond = false;
|
||||
}
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.email
|
||||
) {
|
||||
this.errors.push("Adresse email requise");
|
||||
cond = false;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
getLocationTypesList() {
|
||||
getLocationTypes().then((results) => {
|
||||
this.locationTypes = results.filter(
|
||||
(t) => t.availableForUsers === true,
|
||||
);
|
||||
});
|
||||
},
|
||||
openModal() {
|
||||
this.modal.showModal = true;
|
||||
},
|
||||
saveNewLocation() {
|
||||
if (this.checkForm()) {
|
||||
let body = {
|
||||
type: "location",
|
||||
name: this.selected.name,
|
||||
locationType: {
|
||||
id: this.selected.type.id,
|
||||
type: "location-type",
|
||||
},
|
||||
phonenumber1: this.selected.phonenumber1,
|
||||
phonenumber2: this.selected.phonenumber2,
|
||||
email: this.selected.email,
|
||||
setup() {
|
||||
return {
|
||||
trans,
|
||||
SAVE,
|
||||
ACTIVITY_LOCATION_FIELDS_EMAIL,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
|
||||
ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
|
||||
ACTIVITY_LOCATION_FIELDS_NAME,
|
||||
ACTIVITY_LOCATION_FIELDS_TYPE,
|
||||
ACTIVITY_CHOOSE_LOCATION_TYPE,
|
||||
ACTIVITY_CREATE_NEW_LOCATION,
|
||||
ACTIVITY_EDIT_ADDRESS,
|
||||
ACTIVITY_CREATE_ADDRESS,
|
||||
};
|
||||
if (this.selected.addressId) {
|
||||
body = Object.assign(body, {
|
||||
address: {
|
||||
id: this.selected.addressId,
|
||||
},
|
||||
props: ["availableLocations"],
|
||||
data() {
|
||||
return {
|
||||
errors: [],
|
||||
selected: {
|
||||
type: null,
|
||||
name: null,
|
||||
addressId: null,
|
||||
phonenumber1: null,
|
||||
phonenumber2: null,
|
||||
email: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
makeFetch("POST", "/api/1.0/main/location.json", body)
|
||||
.then((response) => {
|
||||
this.$store.dispatch("addAvailableLocationGroup", {
|
||||
locationGroup: "Localisations nouvellement créées",
|
||||
locations: [response],
|
||||
});
|
||||
this.$store.dispatch("updateLocation", response);
|
||||
this.modal.showModal = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === "ValidationException") {
|
||||
for (let v of error.violations) {
|
||||
this.errors.push(v);
|
||||
}
|
||||
} else {
|
||||
this.errors.push("An error occurred");
|
||||
locationTypes: [],
|
||||
modal: {
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
},
|
||||
addAddress: {
|
||||
options: {
|
||||
button: {
|
||||
text: {
|
||||
create: ACTIVITY_CREATE_ADDRESS,
|
||||
edit: ACTIVITY_EDIT_ADDRESS,
|
||||
},
|
||||
size: "btn-sm",
|
||||
},
|
||||
title: {
|
||||
create: ACTIVITY_CREATE_ADDRESS,
|
||||
edit: ACTIVITY_EDIT_ADDRESS,
|
||||
},
|
||||
},
|
||||
context: {
|
||||
target: {
|
||||
//name, id
|
||||
},
|
||||
edit: false,
|
||||
addressId: null,
|
||||
defaults: window.addaddress,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["activity"]),
|
||||
selectType: {
|
||||
get() {
|
||||
return this.selected.type;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.type = value;
|
||||
},
|
||||
},
|
||||
inputName: {
|
||||
get() {
|
||||
return this.selected.name;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.name = value;
|
||||
},
|
||||
},
|
||||
inputEmail: {
|
||||
get() {
|
||||
return this.selected.email;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.email = value;
|
||||
},
|
||||
},
|
||||
inputPhonenumber1: {
|
||||
get() {
|
||||
return this.selected.phonenumber1;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber1 = value;
|
||||
},
|
||||
},
|
||||
inputPhonenumber2: {
|
||||
get() {
|
||||
return this.selected.phonenumber2;
|
||||
},
|
||||
set(value) {
|
||||
this.selected.phonenumber2 = value;
|
||||
},
|
||||
},
|
||||
hasPhonenumber1() {
|
||||
return (
|
||||
this.selected.phonenumber1 !== null &&
|
||||
this.selected.phonenumber1 !== ""
|
||||
);
|
||||
},
|
||||
showAddAddress() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.addressRequired !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
showContactData() {
|
||||
let cond = false;
|
||||
if (this.selected.type) {
|
||||
if (this.selected.type.contactData !== "never") {
|
||||
cond = true;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
},
|
||||
submitNewAddress(payload) {
|
||||
this.selected.addressId = payload.addressId;
|
||||
this.addAddress.context.addressId = payload.addressId;
|
||||
this.addAddress.context.edit = true;
|
||||
mounted() {
|
||||
this.getLocationTypesList();
|
||||
},
|
||||
methods: {
|
||||
localizeString,
|
||||
checkForm() {
|
||||
let cond = true;
|
||||
this.errors = [];
|
||||
if (!this.selected.type) {
|
||||
this.errors.push("Type de localisation requis");
|
||||
cond = false;
|
||||
} else {
|
||||
if (
|
||||
this.selected.type.addressRequired === "required" &&
|
||||
!this.selected.addressId
|
||||
) {
|
||||
this.errors.push("Adresse requise");
|
||||
cond = false;
|
||||
}
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.phonenumber1
|
||||
) {
|
||||
this.errors.push("Numéro de téléphone requis");
|
||||
cond = false;
|
||||
}
|
||||
if (
|
||||
this.selected.type.contactData === "required" &&
|
||||
!this.selected.email
|
||||
) {
|
||||
this.errors.push("Adresse email requise");
|
||||
cond = false;
|
||||
}
|
||||
}
|
||||
return cond;
|
||||
},
|
||||
getLocationTypesList() {
|
||||
getLocationTypes().then((results) => {
|
||||
this.locationTypes = results.filter(
|
||||
(t) => t.availableForUsers === true,
|
||||
);
|
||||
});
|
||||
},
|
||||
openModal() {
|
||||
this.modal.showModal = true;
|
||||
},
|
||||
saveNewLocation() {
|
||||
if (this.checkForm()) {
|
||||
let body = {
|
||||
type: "location",
|
||||
name: this.selected.name,
|
||||
locationType: {
|
||||
id: this.selected.type.id,
|
||||
type: "location-type",
|
||||
},
|
||||
phonenumber1: this.selected.phonenumber1,
|
||||
phonenumber2: this.selected.phonenumber2,
|
||||
email: this.selected.email,
|
||||
};
|
||||
if (this.selected.addressId) {
|
||||
body = Object.assign(body, {
|
||||
address: {
|
||||
id: this.selected.addressId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
makeFetch("POST", "/api/1.0/main/location.json", body)
|
||||
.then((response) => {
|
||||
this.$store.dispatch("addAvailableLocationGroup", {
|
||||
locationGroup: "Localisations nouvellement créées",
|
||||
locations: [response],
|
||||
});
|
||||
this.$store.dispatch("updateLocation", response);
|
||||
this.modal.showModal = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.name === "ValidationException") {
|
||||
for (let v of error.violations) {
|
||||
this.errors.push(v);
|
||||
}
|
||||
} else {
|
||||
this.errors.push("An error occurred");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
submitNewAddress(payload) {
|
||||
this.selected.addressId = payload.addressId;
|
||||
this.addAddress.context.addressId = payload.addressId;
|
||||
this.addAddress.context.edit = true;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -43,6 +43,7 @@ use DateTime;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Order;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
@@ -140,6 +141,12 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
#[ORM\ManyToMany(targetEntity: Calendar::class, mappedBy: 'persons')]
|
||||
private Collection $calendars;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Calendar>&Selectable<int, Calendar>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'person', targetEntity: Calendar::class)]
|
||||
private Collection $directCalendars;
|
||||
|
||||
/**
|
||||
* The person's center.
|
||||
*
|
||||
@@ -409,6 +416,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
public function __construct()
|
||||
{
|
||||
$this->calendars = new ArrayCollection();
|
||||
$this->directCalendars = new ArrayCollection();
|
||||
$this->accompanyingPeriodParticipations = new ArrayCollection();
|
||||
$this->spokenLanguages = new ArrayCollection();
|
||||
$this->addresses = new ArrayCollection();
|
||||
@@ -869,6 +877,30 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->calendars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next calendars for this person (calendars with start date after today).
|
||||
* Only returns calendars that are directly linked to this person via the person property,
|
||||
* not those linked through AccompanyingPeriods.
|
||||
*
|
||||
* @param int|null $limit Optional limit for the number of results
|
||||
*
|
||||
* @return array<Calendar>
|
||||
*/
|
||||
public function getNextCalendarsForPerson(?int $limit = null): array
|
||||
{
|
||||
$today = new \DateTimeImmutable('today');
|
||||
|
||||
$criteria = Criteria::create()
|
||||
->where(Criteria::expr()->gte('startDate', $today))
|
||||
->orderBy(['startDate' => Order::Ascending]);
|
||||
|
||||
if (null !== $limit) {
|
||||
$criteria->setMaxResults($limit);
|
||||
}
|
||||
|
||||
return $this->directCalendars->matching($criteria)->toArray();
|
||||
}
|
||||
|
||||
public function getCenter(): ?Center
|
||||
{
|
||||
if (null !== $this->centerCurrent) {
|
||||
@@ -1122,7 +1154,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
->where(
|
||||
$expr->eq('shareHousehold', false)
|
||||
)
|
||||
->orderBy(['startDate' => \Doctrine\Common\Collections\Order::Descending]);
|
||||
->orderBy(['startDate' => Order::Descending]);
|
||||
|
||||
return $this->getHouseholdParticipations()
|
||||
->matching($criteria);
|
||||
@@ -1144,7 +1176,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
->where(
|
||||
$expr->eq('shareHousehold', true)
|
||||
)
|
||||
->orderBy(['startDate' => \Doctrine\Common\Collections\Order::Descending, 'id' => \Doctrine\Common\Collections\Order::Descending]);
|
||||
->orderBy(['startDate' => Order::Descending, 'id' => Order::Descending]);
|
||||
|
||||
return $this->getHouseholdParticipations()
|
||||
->matching($criteria);
|
||||
|
||||
@@ -212,4 +212,3 @@
|
||||
</div>
|
||||
</div>
|
||||
{%- endif -%}
|
||||
|
||||
|
||||
@@ -301,6 +301,47 @@
|
||||
'customButtons': { 'after': _self.button_person_after(person), 'before': _self.button_person_before((person)) }
|
||||
}) }}
|
||||
|
||||
{% set calendars = [] %}
|
||||
{% for c in person.getNextCalendarsForPerson(10) %}
|
||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', c) %}
|
||||
{% set calendars = calendars|merge([c]) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if calendars|length > 0 %}
|
||||
<div class="wrap-list periods-list">
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title">
|
||||
<h3>{{ 'chill_calendar.Next calendars'|trans }}</h3>
|
||||
</div>
|
||||
<div class="wl-col list">
|
||||
<div class="calendar-list">
|
||||
<ul class="calendar-list">
|
||||
{% for c in calendars %}
|
||||
<li>
|
||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', c) %}
|
||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', { id: c.id }) }}">
|
||||
<span class="badge bg-secondary">
|
||||
{{ c.startDate|format_datetime('long', 'short') }}
|
||||
</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">
|
||||
{{ c.startDate|format_datetime('long', 'short') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_SEE', person) %}
|
||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_list_by_person', {'id': person.id}) }}" class="calendar-list__global"><i class="fa fa-list"></i></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{#- 'acps' is for AcCompanyingPeriodS #}
|
||||
{%- set acps = [] %}
|
||||
{%- set acpsClosed = [] %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
module.exports = function (encore, entries) {
|
||||
encore.addEntry(
|
||||
"page_wopi_editor",
|
||||
__dirname + "/src/Resources/public/page/editor/index.js",
|
||||
__dirname + "/src/Resources/public/page/editor/index.ts",
|
||||
);
|
||||
encore.addEntry(
|
||||
"mod_reload_page",
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
require("./index.scss");
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
let frameholder = document.getElementById("frameholder");
|
||||
let office_frame = document.createElement("iframe");
|
||||
office_frame.name = "office_frame";
|
||||
office_frame.id = "office_frame";
|
||||
|
||||
// The title should be set for accessibility
|
||||
office_frame.title = "Office Frame";
|
||||
|
||||
// This attribute allows true fullscreen mode in slideshow view
|
||||
// when using PowerPoint's 'view' action.
|
||||
office_frame.setAttribute("allowfullscreen", "true");
|
||||
|
||||
// The sandbox attribute is needed to allow automatic redirection to the O365 sign-in page in the business user flow
|
||||
office_frame.setAttribute(
|
||||
"sandbox",
|
||||
"allow-downloads allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-top-navigation allow-popups-to-escape-sandbox",
|
||||
);
|
||||
frameholder.appendChild(office_frame);
|
||||
|
||||
document.getElementById("office_form").submit();
|
||||
|
||||
const url = new URL(editor_url);
|
||||
const editor_domain = url.origin;
|
||||
|
||||
window.addEventListener("message", function (message) {
|
||||
if (message.origin !== editor_domain) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = JSON.parse(message.data);
|
||||
|
||||
if ("UI_Close" === data.MessageId) {
|
||||
closeEditor();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function closeEditor() {
|
||||
let params = new URLSearchParams(window.location.search),
|
||||
returnPath = params.get("returnPath");
|
||||
|
||||
window.location.assign(returnPath);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import "./index.scss";
|
||||
|
||||
// Provided by the server-side template
|
||||
declare const editor_url: string;
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
const frameholder = document.getElementById("frameholder");
|
||||
const office_frame = document.createElement("iframe");
|
||||
office_frame.name = "office_frame";
|
||||
office_frame.id = "office_frame";
|
||||
|
||||
// The title should be set for accessibility
|
||||
office_frame.title = "Office Frame";
|
||||
|
||||
// This attribute allows true fullscreen mode in slideshow view
|
||||
// when using PowerPoint's 'view' action.
|
||||
office_frame.setAttribute("allowfullscreen", "true");
|
||||
|
||||
// The sandbox attribute is needed to allow automatic redirection to the O365 sign-in page in the business user flow
|
||||
office_frame.setAttribute(
|
||||
"sandbox",
|
||||
"allow-downloads allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-top-navigation allow-popups-to-escape-sandbox",
|
||||
);
|
||||
|
||||
office_frame.setAttribute(
|
||||
"allow",
|
||||
"clipboard-read *; clipboard-write *; fullscreen *",
|
||||
);
|
||||
if (frameholder) {
|
||||
frameholder.appendChild(office_frame);
|
||||
}
|
||||
|
||||
const officeForm = document.getElementById(
|
||||
"office_form",
|
||||
) as HTMLFormElement | null;
|
||||
officeForm?.submit();
|
||||
|
||||
const url = new URL(editor_url);
|
||||
const editor_domain = url.origin;
|
||||
|
||||
window.addEventListener("message", function (message: MessageEvent) {
|
||||
if (message.origin !== editor_domain) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data: { MessageId: "UI_Close" | null; data: string };
|
||||
try {
|
||||
data =
|
||||
typeof message.data === "string"
|
||||
? JSON.parse(message.data)
|
||||
: message.data;
|
||||
} catch (e: unknown) {
|
||||
console.error("error while parsing data from message UI_CLOSE", e);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("UI_Close" === data.MessageId) {
|
||||
closeEditor();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function closeEditor(): void {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const returnPath = params.get("returnPath") ?? "/";
|
||||
|
||||
window.location.assign(returnPath);
|
||||
}
|
||||
Reference in New Issue
Block a user