Merge branch 'master' into ticket-app-master

# Conflicts:
#	.junie/guidelines.md
#	src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/components/SocialIssuesAcc.vue
#	src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/App.vue
#	src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/App2.vue
#	src/Bundle/ChillDocStoreBundle/Resources/public/types/generic_doc.ts
#	src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue
#	src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue
#	src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/App.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/App.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/AttachmentList.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue
#	src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
#	src/Bundle/ChillMainBundle/Resources/views/layout.html.twig
#	src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml
#	src/Bundle/ChillPersonBundle/Resources/public/mod/DuplicateSelector/AccompanyingPeriodWorkSelector.ts
#	src/Bundle/ChillPersonBundle/Resources/public/types.ts
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/components/FormEvaluation.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkList.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkSelectorModal.vue
#	src/Bundle/ChillPersonBundle/Resources/public/vuejs/_js/i18n.ts
#	src/Bundle/ChillPersonBundle/translations/messages.fr.yml
This commit is contained in:
2025-11-05 13:19:05 +01:00
195 changed files with 8111 additions and 4168 deletions

View File

@@ -66,6 +66,9 @@ class ListActivityHelper
->leftJoin('activity.location', 'location')
->addSelect('location.name AS locationName')
->addSelect('activity.sentReceived')
->addSelect('activity.comment.comment AS commentText')
->addSelect('activity.comment.date AS commentDate')
->addSelect('JSON_BUILD_OBJECT(\'uid\', activity.comment.userId, \'d\', activity.comment.date) AS commentUser')
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
->addSelect('activity.createdAt')
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
@@ -87,6 +90,8 @@ class ListActivityHelper
'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY.$key),
'commentDate' => $this->dateTimeHelper->getLabel(self::MSG_KEY.'comment_date'),
'commentUser' => $this->userHelper->getLabel($key, $values, self::MSG_KEY.'comment_user'),
'attendeeName' => function ($value) {
if ('_header' === $value) {
return 'Attendee';
@@ -176,6 +181,9 @@ class ListActivityHelper
'usersNames',
'thirdPartiesIds',
'thirdPartiesNames',
'commentText',
'commentDate',
'commentUser',
'createdBy',
'createdAt',
'updatedBy',

View File

@@ -90,7 +90,9 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
public function getFormDefaultData(): array
{
return [];
return [
'reasons' => [],
];
}
public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array

View File

@@ -42,6 +42,8 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
{
error_log('alterQuery called with data: '.json_encode(array_keys($data)));
// create a subquery for activity
$sqb = $qb->getEntityManager()->createQueryBuilder();
$sqb->select('1')
@@ -59,7 +61,6 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
if (\in_array('activity', $qb->getAllAliases(), true)) {
$sqb->andWhere('activity_person_having_activity.id = activity.id');
}
if (isset($data['reasons']) && [] !== $data['reasons']) {
// add clause activity reason
$sqb->join('activity_person_having_activity.reasons', 'reasons_person_having_activity');
@@ -124,12 +125,38 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function normalizeFormData(array $formData): array
{
return ['date_from_rolling' => $formData['date_from_rolling']->normalize(), 'date_to_rolling' => $formData['date_to_rolling']->normalize()];
$normalized = [
'date_from_rolling' => $formData['date_from_rolling']->normalize(),
'date_to_rolling' => $formData['date_to_rolling']->normalize(),
'reasons' => [],
];
if (isset($formData['reasons']) && [] !== $formData['reasons']) {
$normalized['reasons'] = array_map(
fn (ActivityReason $reason) => $reason->getId(),
$formData['reasons']
);
}
return $normalized;
}
public function denormalizeFormData(array $formData, int $fromVersion): array
{
return ['date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']), 'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling'])];
$denormalized = [
'date_from_rolling' => RollingDate::fromNormalized($formData['date_from_rolling']),
'date_to_rolling' => RollingDate::fromNormalized($formData['date_to_rolling']),
'reasons' => [],
];
if (isset($formData['reasons']) && [] !== $formData['reasons']) {
$denormalized['reasons'] = array_map(
fn ($id) => $this->activityReasonRepository->find($id),
$formData['reasons']
);
}
return $denormalized;
}
public function getFormDefaultData(): array
@@ -143,10 +170,12 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function describeAction($data, ExportGenerationContext $context): array
{
$reasons = $data['reasons'] ?? [];
return [
[] === $data['reasons'] ?
'export.filter.person_between_dates.describe_action_with_no_subject'
: 'export.filter.person_between_dates.describe_action_with_subject',
[] === $reasons ?
'export.filter.activity.describe_action_with_no_subject'
: 'export.filter.activity.describe_action_with_subject',
[
'date_from' => $this->rollingDateConverter->convert($data['date_from_rolling']),
'date_to' => $this->rollingDateConverter->convert($data['date_to_rolling']),
@@ -154,7 +183,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
', ',
array_map(
fn (ActivityReason $r): string => '"'.$this->translatableStringHelper->localize($r->getName()).'"',
$data['reasons']
$reasons
)
),
],
@@ -168,6 +197,7 @@ final readonly class PersonHavingActivityBetweenDateFilter implements ExportElem
public function validateForm($data, ExecutionContextInterface $context): void
{
error_log('validateForm called with data: '.json_encode(array_keys($data)));
if ($this->rollingDateConverter->convert($data['date_from_rolling'])
>= $this->rollingDateConverter->convert($data['date_to_rolling'])) {
$context->buildViolation('export.filter.activity.person_between_dates.date mismatch')

View File

@@ -1,98 +1,103 @@
<template>
<teleport to="#social-issues-acc">
<div class="mb-3 row">
<div class="col-4">
<label :class="socialIssuesClassList">{{
trans(ACTIVITY_SOCIAL_ISSUES)
}}</label>
</div>
<div class="col-8">
<check-social-issue
v-for="issue in socialIssuesList"
:key="issue.id"
:issue="issue"
:selection="socialIssuesSelected"
@updateSelected="updateIssuesSelected"
>
</check-social-issue>
<teleport to="#social-issues-acc">
<div class="mb-3 row">
<div class="col-4">
<label :class="socialIssuesClassList">{{
trans(ACTIVITY_SOCIAL_ISSUES)
}}</label>
</div>
<div class="col-8">
<check-social-issue
v-for="issue in socialIssuesList"
:key="issue.id"
:issue="issue"
:selection="socialIssuesSelected"
@updateSelected="updateIssuesSelected"
>
</check-social-issue>
<div class="my-3">
<VueMultiselect
name="otherIssues"
label="text"
track-by="id"
open-direction="bottom"
:close-on-select="true"
:preserve-search="false"
:reset-after="true"
:hide-selected="true"
:taggable="false"
:multiple="false"
:searchable="true"
:allow-empty="true"
:show-labels="false"
:loading="issueIsLoading"
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
:options="socialIssuesOther"
@select="addIssueInList"
>
</VueMultiselect>
</div>
</div>
</div>
<div class="mb-3 row">
<div class="col-4">
<label :class="socialActionsClassList">{{
trans(ACTIVITY_SOCIAL_ACTIONS)
}}</label>
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
<div class="my-3">
<VueMultiselect
name="otherIssues"
label="text"
track-by="id"
open-direction="bottom"
:close-on-select="true"
:preserve-search="false"
:reset-after="true"
:hide-selected="true"
:taggable="false"
:multiple="false"
:searchable="true"
:allow-empty="true"
:show-labels="false"
:loading="issueIsLoading"
:placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
:options="socialIssuesOther"
@select="addIssueInList"
>
</VueMultiselect>
</div>
</div>
</div>
<span
v-else-if="socialIssuesSelected.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
</span>
<div class="mb-3 row">
<div class="col-4">
<label :class="socialActionsClassList">{{
trans(ACTIVITY_SOCIAL_ACTIONS)
}}</label>
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i
class="chill-green fa fa-circle-o-notch fa-spin fa-lg"
></i>
</div>
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length || socialActionsSelected.length)
"
>
<div
id="actionsList"
v-for="group in socialActionsList"
:key="group.issue"
>
<span class="badge bg-chill-l-gray text-dark">{{
group.issue
}}</span>
<check-social-action
v-for="action in group.actions"
:key="action.id"
:action="action"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
>
</check-social-action>
</div>
</template>
<span
v-else-if="socialIssuesSelected.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
</span>
<span
v-else-if="actionAreLoaded && socialActionsList.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
</span>
</div>
</div>
</teleport>
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length ||
socialActionsSelected.length)
"
>
<div
id="actionsList"
v-for="group in socialActionsList"
:key="group.issue"
>
<span class="badge bg-chill-l-gray text-dark">{{
group.issue
}}</span>
<check-social-action
v-for="action in group.actions"
:key="action.id"
:action="action"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
>
</check-social-action>
</div>
</template>
<span
v-else-if="
actionAreLoaded && socialActionsList.length === 0
"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
</span>
</div>
</div>
</teleport>
</template>
<script>
@@ -101,153 +106,175 @@ import CheckSocialIssue from "./SocialIssuesAcc/CheckSocialIssue.vue";
import CheckSocialAction from "./SocialIssuesAcc/CheckSocialAction.vue";
import { getSocialIssues, getSocialActionByIssue } from "../api.js";
import {
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
trans,
} from "translator";
export default {
name: "SocialIssuesAcc",
components: {
CheckSocialIssue,
CheckSocialAction,
VueMultiselect,
},
setup() {
return {
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
};
},
data() {
return {
issueIsLoading: false,
actionIsLoading: false,
actionAreLoaded: false,
socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`,
socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`,
};
},
computed: {
socialIssuesList() {
return this.$store.state.activity.accompanyingPeriod.socialIssues;
name: "SocialIssuesAcc",
components: {
CheckSocialIssue,
CheckSocialAction,
VueMultiselect,
},
socialIssuesSelected() {
return this.$store.state.activity.socialIssues;
setup() {
return {
trans,
ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
ACTIVITY_SOCIAL_ACTIONS,
ACTIVITY_SOCIAL_ISSUES,
ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
};
},
socialIssuesOther() {
return this.$store.state.socialIssuesOther;
data() {
return {
issueIsLoading: false,
actionIsLoading: false,
actionAreLoaded: false,
socialIssuesClassList: {
"col-form-label": true,
required: false,
},
socialActionsClassList: {
"col-form-label": true,
required: false,
},
};
},
socialActionsList() {
return this.$store.getters.socialActionsListSorted;
computed: {
socialIssuesList() {
return this.$store.state.activity.accompanyingPeriod.socialIssues;
},
socialIssuesSelected() {
return this.$store.state.activity.socialIssues;
},
socialIssuesOther() {
return this.$store.state.socialIssuesOther;
},
socialActionsList() {
return this.$store.getters.socialActionsListSorted;
},
socialActionsSelected() {
return this.$store.state.activity.socialActions;
},
},
socialActionsSelected() {
return this.$store.state.activity.socialActions;
},
},
mounted() {
/* Load other issues in multiselect */
this.issueIsLoading = true;
this.actionAreLoaded = false;
getSocialIssues().then((response) => {
/* Add issues to the store */
this.$store.commit("updateIssuesOther", response);
/* Add in list the issues already associated (if not yet listed) */
this.socialIssuesSelected.forEach((issue) => {
if (
this.socialIssuesList.filter((i) => i.id === issue.id).length !== 1
) {
this.$store.commit("addIssueInList", issue);
mounted() {
/* Load classNames after element is present */
const socialActionsEl = document.querySelector(
"input#chill_activitybundle_activity_socialActions",
);
if (socialActionsEl && socialActionsEl.hasAttribute("required")) {
this.socialActionsClassList.required = true;
}
});
/* Remove from multiselect the issues that are not yet in the checkbox list */
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
});
const socialIssuesEl = document.querySelector(
"input#chill_activitybundle_activity_socialIssues",
);
if (socialIssuesEl && socialIssuesEl.hasAttribute("required")) {
this.socialIssuesClassList.required = true;
}
/* Filter issues */
this.$store.commit("filterList", "issues");
/* Load other issues in multiselect */
this.issueIsLoading = true;
this.actionAreLoaded = false;
/* Add in list the actions already associated (if not yet listed) */
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
});
getSocialIssues().then((response) => {
/* Add issues to the store */
this.$store.commit("updateIssuesOther", response);
/* Filter actions */
this.$store.commit("filterList", "actions");
/* Add in list the issues already associated (if not yet listed) */
this.socialIssuesSelected.forEach((issue) => {
if (
this.socialIssuesList.filter((i) => i.id === issue.id)
.length !== 1
) {
this.$store.commit("addIssueInList", issue);
}
});
this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
});
},
methods: {
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
/* Remove from multiselect the issues that are not yet in the checkbox list */
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
});
/* Filter issues */
this.$store.commit("filterList", "issues");
/* Add in list the actions already associated (if not yet listed) */
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
});
/* Filter actions */
this.$store.commit("filterList", "actions");
this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
});
},
methods: {
/* When choosing an issue in multiselect, add it in checkboxes (as selected),
remove it from multiselect, and add socialActions concerned
*/
addIssueInList(value) {
//console.log('addIssueInList', value);
this.$store.commit("addIssueInList", value);
this.$store.commit("removeIssueInOther", value);
this.$store.dispatch("addIssueSelected", value);
this.updateActionsList();
},
/* Update value for selected issues checkboxes
*/
updateIssuesSelected(issues) {
//console.log('updateIssuesSelected', issues);
this.$store.dispatch("updateIssuesSelected", issues);
this.updateActionsList();
},
/* Update value for selected actions checkboxes
*/
updateActionsSelected(actions) {
//console.log('updateActionsSelected', actions);
this.$store.dispatch("updateActionsSelected", actions);
},
/* Add socialActions concerned: after reset, loop on each issue selected
addIssueInList(value) {
//console.log('addIssueInList', value);
this.$store.commit("addIssueInList", value);
this.$store.commit("removeIssueInOther", value);
this.$store.dispatch("addIssueSelected", value);
this.updateActionsList();
},
/* Update value for selected issues checkboxes
*/
updateIssuesSelected(issues) {
//console.log('updateIssuesSelected', issues);
this.$store.dispatch("updateIssuesSelected", issues);
this.updateActionsList();
},
/* Update value for selected actions checkboxes
*/
updateActionsSelected(actions) {
//console.log('updateActionsSelected', actions);
this.$store.dispatch("updateActionsSelected", actions);
},
/* Add socialActions concerned: after reset, loop on each issue selected
to get social actions concerned
*/
updateActionsList() {
this.resetActionsList();
this.socialIssuesSelected.forEach((item) => {
this.actionIsLoading = true;
getSocialActionByIssue(item.id).then(
(actions) =>
new Promise((resolve) => {
actions.results.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
updateActionsList() {
this.resetActionsList();
this.socialIssuesSelected.forEach((item) => {
this.actionIsLoading = true;
getSocialActionByIssue(item.id).then(
(actions) =>
new Promise((resolve) => {
actions.results.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
this.$store.commit("filterList", "actions");
this.$store.commit("filterList", "actions");
this.actionIsLoading = false;
this.actionAreLoaded = true;
resolve();
}),
);
}, this);
this.actionIsLoading = false;
this.actionAreLoaded = true;
resolve();
}),
);
}, this);
},
/* Reset socialActions List: flush list and restore selected actions
*/
resetActionsList() {
this.$store.commit("resetActionsList");
this.actionAreLoaded = false;
this.socialActionsSelected.forEach((item) => {
this.$store.commit("addActionInList", item);
}, this);
},
},
/* Reset socialActions List: flush list and restore selected actions
*/
resetActionsList() {
this.$store.commit("resetActionsList");
this.actionAreLoaded = false;
this.socialActionsSelected.forEach((item) => {
this.$store.commit("addActionInList", item);
}, this);
},
},
};
</script>
@@ -257,18 +284,18 @@ export default {
@import "ChillMainAssets/chill/scss/chill_variables";
span.multiselect__single {
display: none !important;
display: none !important;
}
#actionsList {
border-radius: 0.5rem;
padding: 1rem;
margin: 0.5rem;
background-color: whitesmoke;
border-radius: 0.5rem;
padding: 1rem;
margin: 0.5rem;
background-color: whitesmoke;
}
span.badge {
margin-bottom: 0.5rem;
@include badge_social($social-issue-color);
margin-bottom: 0.5rem;
@include badge_social($social-issue-color);
}
</style>

View File

@@ -10,7 +10,7 @@ Attendee: Présence de l'usager
attendee: présence de l'usager
list_reasons: liste des sujets
user_username: nom de l'utilisateur
circle_name: nom du cercle
circle_name: nom du service
Remark: Commentaire
No comments: Aucun commentaire
Add a new activity: Ajouter une nouvel échange
@@ -20,7 +20,7 @@ not present: absent
Delete: Supprimer
Update: Mettre à jour
Update activity: Modifier l'échange
Scope: Cercle
Scope: Service
Activity data: Données de l'échange
Activity location: Localisation de l'échange
No reason associated: Aucun sujet
@@ -398,13 +398,15 @@ export:
sent received: Envoyé ou reçu
emergency: Urgence
accompanying course id: Identifiant du parcours
course circles: Cercles du parcours
course circles: Services du parcours
travelTime: Durée de déplacement
durationTime: Durée
id: Identifiant
List activities linked to an accompanying course: Liste les échanges liés à un parcours en fonction de différents filtres.
List activity linked to a course: Liste des échanges liés à un parcours
commentText: Commentaire
comment_date: Date de la dernière édition du commentaire
comment_user: Dernière édition par
filter:
activity: