Merge branch 'refs/heads/master' into 339-partage-d'export-enregistré

This commit is contained in:
2025-04-08 17:30:38 +02:00
45 changed files with 622 additions and 210 deletions

View File

@@ -68,14 +68,23 @@
socialActionsSelected.length)
"
>
<check-social-action
v-for="action in socialActionsList"
:key="action.id"
:action="action"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
<div
id="actionsList"
v-for="group in socialActionsList"
:key="group.issue"
>
</check-social-action>
<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
@@ -149,53 +158,44 @@ export default {
},
},
mounted() {
/* Load others issues in multiselect
*/
/* Load other issues in multiselect */
this.issueIsLoading = true;
this.actionAreLoaded = false;
getSocialIssues().then(
(response) =>
new Promise((resolve) => {
this.$store.commit("updateIssuesOther", response.results);
/* 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);
getSocialIssues().then((response) => {
/* Add issues to the store */
this.$store.commit("updateIssuesOther", response);
/* Remove from multiselect the issues that are not yet in checkbox list
*/
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
}, this);
/* 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);
}
});
/* Filter issues
*/
this.$store.commit("filterList", "issues");
/* Remove from multiselect the issues that are not yet in the checkbox list */
this.socialIssuesList.forEach((issue) => {
this.$store.commit("removeIssueInOther", issue);
});
/* Add in list the actions already associated (if not yet listed)
*/
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
/* Filter issues */
this.$store.commit("filterList", "issues");
/* Filter issues
*/
this.$store.commit("filterList", "actions");
/* Add in list the actions already associated (if not yet listed) */
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
});
this.issueIsLoading = false;
this.actionAreLoaded = true;
this.updateActionsList();
resolve();
}),
);
/* 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),
@@ -258,7 +258,23 @@ export default {
</script>
<style lang="scss" scoped>
@import "ChillMainAssets/module/bootstrap/shared";
@import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables";
span.multiselect__single {
display: none !important;
}
#actionsList {
border-radius: 0.5rem;
padding: 1rem;
margin: 0.5rem;
background-color: whitesmoke;
}
span.badge {
margin-bottom: 0.5rem;
@include badge_social($social-issue-color);
}
</style>

View File

@@ -10,7 +10,9 @@
:value="action"
/>
<label class="form-check-label" :for="action.id">
<span class="badge bg-light text-dark">{{ action.text }}</span>
<span class="badge bg-light text-dark" :title="action.text">{{
action.text
}}</span>
</label>
</div>
</span>
@@ -43,5 +45,9 @@ span.badge {
font-size: 95%;
margin-bottom: 5px;
margin-right: 1em;
max-width: 100%; /* Adjust as needed */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -124,9 +124,19 @@ const store = createStore({
);
},
socialActionsListSorted(state) {
return [...state.socialActionsList].sort(
(a, b) => a.ordering - b.ordering,
);
return [...state.socialActionsList]
.sort((a, b) => a.ordering - b.ordering)
.reduce((acc, action) => {
const issueText = action.issue?.text || "Uncategorized";
// Find if the group for the issue already exists
let group = acc.find((item) => item.issue === issueText);
if (!group) {
group = { issue: issueText, actions: [] };
acc.push(group);
}
group.actions.push(action);
return acc;
}, []);
},
},
mutations: {

View File

@@ -96,13 +96,13 @@
</div>
</div>
<FullCalendar :options="calendarOptions" ref="calendarRef">
<template v-slot:eventContent="arg: EventApi">
<template v-slot:eventContent="{ arg }: { arg: { event: EventApi } }">
<span :class="eventClasses(arg.event)">
<b v-if="arg.event.extendedProps.is === 'remote'">{{
arg.event.title
}}</b>
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }} -
>{{ arg.event.startStr }} -
{{ arg.event.extendedProps.locationName }}</b
>
<b v-else-if="arg.event.extendedProps.is === 'local'">{{

View File

@@ -91,10 +91,7 @@
class="col-5 p-0 text-center turnSignature"
>
<button
:disabled="
userSignatureZone === null ||
userSignatureZone?.index < 1
"
:disabled="isFirstSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(-1)"
>
@@ -102,9 +99,7 @@
</button>
<span>|</span>
<button
:disabled="
userSignatureZone?.index >= signature.zones.length - 1
"
:disabled="isLastSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(1)"
>
@@ -200,10 +195,7 @@
class="col-4 d-xl-none text-center turnSignature p-0"
>
<button
:disabled="
userSignatureZone === null ||
userSignatureZone?.index < 1
"
:disabled="!hasSignatureZoneSelected"
class="btn btn-light btn-sm"
@click="turnSignature(-1)"
>
@@ -211,9 +203,7 @@
</button>
<span>|</span>
<button
:disabled="
userSignatureZone?.index >= signature.zones.length - 1
"
:disabled="isLastSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(1)"
>
@@ -225,10 +215,7 @@
class="col-4 d-none d-xl-flex p-0 text-center turnSignature"
>
<button
:disabled="
userSignatureZone === null ||
userSignatureZone?.index < 1
"
:disabled="isFirstSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(-1)"
>
@@ -236,9 +223,7 @@
</button>
<span>|</span>
<button
:disabled="
userSignatureZone?.index >= signature.zones.length - 1
"
:disabled="isLastSignatureZone"
class="btn btn-light btn-sm"
@click="turnSignature(1)"
>
@@ -333,7 +318,7 @@
</template>
<script setup lang="ts">
import { ref, Ref, reactive } from "vue";
import { ref, Ref, computed } from "vue";
import { useToast } from "vue-toast-notification";
import "vue-toast-notification/dist/theme-sugar.css";
import {
@@ -351,18 +336,15 @@ import {
PDFPageProxy,
} from "pdfjs-dist/types/src/display/api";
// @ts-ignore
// @ts-ignore incredible but the console.log is needed
import * as PdfWorker from "pdfjs-dist/build/pdf.worker.mjs";
console.log(PdfWorker); // incredible but this is needed
console.log(PdfWorker);
// import { PdfWorker } from 'pdfjs-dist/build/pdf.worker.mjs'
// pdfjsLib.GlobalWorkerOptions.workerSrc = PdfWorker;
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import {
download_and_decrypt_doc,
download_doc_as_pdf,
} from "../StoredObjectButton/helpers";
import { download_doc_as_pdf } from "../StoredObjectButton/helpers";
pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs-dist/build/pdf.worker.mjs";
@@ -433,6 +415,20 @@ const $toast = useToast();
const signature = window.signature;
const isFirstSignatureZone = () =>
userSignatureZone.value?.index ? userSignatureZone.value.index < 1 : false;
const isLastSignatureZone = () =>
userSignatureZone.value?.index
? userSignatureZone.value.index >= signature.zones.length - 1
: false;
/**
* Return true if the user has selected a user zone (existing on the doc or created by the user)
*/
const hasSignatureZoneSelected = computed<boolean>(
() => userSignatureZone.value !== null,
);
const setZoomLevel = async (zoomLevel: string) => {
zoom.value = Number.parseFloat(zoomLevel);
await resetPages();
@@ -754,7 +750,7 @@ const confirmSign = () => {
zone: userSignatureZone.value,
};
makeFetch("POST", url, body)
.then((r) => {
.then(() => {
checkForReady();
})
.catch((error) => {
@@ -776,9 +772,7 @@ const undoSign = async () => {
};
const toggleAddZone = () => {
canvasEvent.value === "select"
? (canvasEvent.value = "add")
: (canvasEvent.value = "select");
canvasEvent.value = canvasEvent.value === "select" ? "add" : "select";
};
const addZoneEvent = async (e: PointerEvent, canvas: HTMLCanvasElement) => {

View File

@@ -28,6 +28,10 @@ const open = () => {
state.opened = true;
};
const onRestoreVersion = (payload: {
newVersion: StoredObjectVersionWithPointInTime;
}) => emit("restoreVersion", payload);
defineExpose({ open });
</script>
<template>
@@ -42,9 +46,7 @@ defineExpose({ open });
:versions="props.versions"
:can-edit="canEdit"
:stored-object="storedObject"
@restore-version="
(payload) => emit('restoreVersion', payload)
"
@restore-version="onRestoreVersion"
></history-button-list>
</template>
</modal>

View File

@@ -63,7 +63,6 @@ abstract class AbstractCRUDController extends AbstractController
parent::getSubscribedServices(),
[
'chill_main.paginator_factory' => PaginatorFactory::class,
ManagerRegistry::class => ManagerRegistry::class,
'translator' => TranslatorInterface::class,
AuthorizationHelper::class => AuthorizationHelper::class,
EventDispatcherInterface::class => EventDispatcherInterface::class,
@@ -213,7 +212,7 @@ abstract class AbstractCRUDController extends AbstractController
protected function getManagerRegistry(): ManagerRegistry
{
return $this->container->get(ManagerRegistry::class);
return $this->container->get('doctrine');
}
/**
@@ -226,7 +225,7 @@ abstract class AbstractCRUDController extends AbstractController
protected function getValidator(): ValidatorInterface
{
return $this->get('validator');
return $this->container->get('validator');
}
/**

View File

@@ -75,8 +75,8 @@ final class UserGroupRepository implements UserGroupRepositoryInterface, LocaleA
->setWhereClauses('
ug.active AND (
SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) > 0.15
OR ug.label->>? LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
', [$pattern, $lang, $pattern, $lang]);
OR LOWER(UNACCENT(ug.label->>?)) LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
', [$pattern, $lang, $lang, $pattern]);
return $query;
}

View File

@@ -22,12 +22,12 @@ function loadDynamicPicker(element) {
? JSON.parse(input.value)
: input.value === "[]" || input.value === ""
? null
: [JSON.parse(input.value)];
(suggested = JSON.parse(el.dataset.suggested)),
(as_id = parseInt(el.dataset.asId) === 1),
(submit_on_adding_new_entity =
parseInt(el.dataset.submitOnAddingNewEntity) === 1);
label = el.dataset.label;
: [JSON.parse(input.value)],
suggested = JSON.parse(el.dataset.suggested),
as_id = parseInt(el.dataset.asId) === 1,
submit_on_adding_new_entity =
parseInt(el.dataset.submitOnAddingNewEntity) === 1,
label = el.dataset.label;
if (!isMultiple) {
if (input.value === "[]") {
@@ -173,7 +173,7 @@ document.addEventListener("pick-entity-type-action", function (e) {
}
});
document.addEventListener("DOMContentLoaded", function (e) {
document.addEventListener("DOMContentLoaded", function () {
loadDynamicPicker(document);
});

View File

@@ -45,6 +45,10 @@ const onPickGenericDoc = ({
}) => {
emit("pickGenericDoc", { genericDoc });
};
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
emit("removeAttachment", payload);
};
</script>
<template>
@@ -56,7 +60,7 @@ const onPickGenericDoc = ({
></pick-generic-doc-modal>
<attachment-list
:attachments="props.attachments"
@removeAttachment="(payload) => emit('removeAttachment', payload)"
@removeAttachment="onRemoveAttachment"
></attachment-list>
<ul class="record_actions">
<li>

View File

@@ -72,6 +72,14 @@ const placeTrans = (str: string): string => {
}
};
const onPickDocument = (payload: {
genericDoc: GenericDocForAccompanyingPeriod;
}) => emit("pickGenericDoc", payload);
const onRemoveGenericDoc = (payload: {
genericDoc: GenericDocForAccompanyingPeriod;
}) => emit("removeGenericDoc", payload);
const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
if (false === loaded.value) {
return [];
@@ -245,10 +253,8 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
:accompanying-period-id="accompanyingPeriodId"
:genericDoc="g"
:is-picked="isPicked(g)"
@pickGenericDoc="(payload) => emit('pickGenericDoc', payload)"
@removeGenericDoc="
(payload) => emit('removeGenericDoc', payload)
"
@pickGenericDoc="onPickDocument"
@removeGenericDoc="onRemoveGenericDoc"
></pick-generic-doc-item>
</div>
<div v-else class="text-center chill-no-data-statement">

View File

@@ -34,6 +34,7 @@ class GenderDocGenNormalizer implements ContextAwareNormalizerInterface, Normali
'id' => $gender->getId(),
'label' => $this->translatableStringHelper->localize($gender->getLabel()),
'genderTranslation' => $gender->getGenderTranslation(),
'type' => 'chill_main_gender',
];
}
}

View File

@@ -68,8 +68,8 @@ class AddressReferenceBEFromBestAddress
$csv->setDelimiter(',');
$csv->setHeaderOffset(0);
$stmt = Statement::create()
->process($csv);
$stmt = new Statement();
$stmt = $stmt->process($csv);
foreach ($stmt as $record) {
$this->baseImporter->importAddress(

View File

@@ -55,32 +55,32 @@ class AddressReferenceFromBAN
$csv = Reader::createFromStream($csvDecompressed);
$csv->setDelimiter(';')->setHeaderOffset(0);
$stmt = Statement::create()
->process($csv, [
'id',
'id_fantoir',
'numero',
'rep',
'nom_voie',
'code_postal',
'code_insee',
'nom_commune',
'code_insee_ancienne_commune',
'nom_ancienne_commune',
'x',
'y',
'lon',
'lat',
'type_position',
'alias',
'nom_ld',
'libelle_acheminement',
'nom_afnor',
'source_position',
'source_nom_voie',
'certification_commune',
'cad_parcelles',
]);
$stmt = new Statement();
$stmt = $stmt->process($csv, [
'id',
'id_fantoir',
'numero',
'rep',
'nom_voie',
'code_postal',
'code_insee',
'nom_commune',
'code_insee_ancienne_commune',
'nom_ancienne_commune',
'x',
'y',
'lon',
'lat',
'type_position',
'alias',
'nom_ld',
'libelle_acheminement',
'nom_afnor',
'source_position',
'source_nom_voie',
'certification_commune',
'cad_parcelles',
]);
foreach ($stmt as $record) {
$this->baseImporter->importAddress(

View File

@@ -43,17 +43,17 @@ class AddressReferenceFromBano
$csv = Reader::createFromStream($file);
$csv->setDelimiter(',');
$stmt = Statement::create()
->process($csv, [
'refId',
'streetNumber',
'street',
'postcode',
'city',
'_o',
'lat',
'lon',
]);
$stmt = new Statement();
$stmt = $stmt->process($csv, [
'refId',
'streetNumber',
'street',
'postcode',
'city',
'_o',
'lat',
'lon',
]);
foreach ($stmt as $record) {
$this->baseImporter->importAddress(

View File

@@ -54,7 +54,8 @@ class AddressReferenceLU
private function process_address(Reader $csv, ?string $sendAddressReportToEmail = null): void
{
$stmt = Statement::create()->process($csv);
$stmt = new Statement();
$stmt = $stmt->process($csv);
foreach ($stmt as $record) {
$this->addressBaseImporter->importAddress(
$record['id_geoportail'],
@@ -74,7 +75,8 @@ class AddressReferenceLU
private function process_postal_code(Reader $csv): void
{
$stmt = Statement::create()->process($csv);
$stmt = new Statement();
$stmt = $stmt->process($csv);
$arr_postal_codes = [];
foreach ($stmt as $record) {
if (false === \array_key_exists($record['code_postal'], $arr_postal_codes)) {

View File

@@ -61,6 +61,7 @@ final class GenderDocGenNormalizerTest extends TestCase
'id' => 1,
'label' => 'homme',
'genderTranslation' => GenderEnum::MALE,
'type' => 'chill_main_gender',
];
$this->assertEquals($expected, $this->normalizer->normalize($gender));

View File

@@ -943,6 +943,16 @@ paths:
description: "ok"
401:
description: "Unauthorized"
/1.0/main/gender.json:
get:
tags:
- gender
summary: Return all gender types
responses:
200:
description: "ok"
401:
description: "Unauthorized"
/1.0/main/user-job.json:
get:
tags:

View File

@@ -33,18 +33,7 @@ const getUserJobs = () => fetchResults("/api/1.0/main/user-job.json");
const getSocialIssues = () => {
const url = `/api/1.0/person/social-work/social-issue.json`;
return fetch(url).then((response) => {
if (response.ok) {
return response.json();
}
throw {
msg: "Error while retriving Social Issues.",
sta: response.status,
txt: response.statusText,
err: new Error(),
body: response.body,
};
});
return fetchResults(url);
};
const whoami = () => {

View File

@@ -29,7 +29,7 @@
<script>
import VueMultiselect from "vue-multiselect";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
import { mapGetters, mapState } from "vuex";
export default {
@@ -51,16 +51,11 @@ export default {
},
methods: {
getOptions() {
const url = `/api/1.0/person/social-work/social-issue.json`;
makeFetch("GET", url)
.then((response) => {
this.options = response.results;
return response;
})
.catch((error) => {
commit("catchError", error);
this.$toast.open({ message: error.txt });
});
fetchResults(`/api/1.0/person/social-work/social-issue.json`).then(
(response) => {
this.options = response;
},
);
},
updateSocialIssues(value) {
this.$store

View File

@@ -6,7 +6,7 @@ import {
} from "ChillMainAssets/chill/js/date";
import { findSocialActionsBySocialIssue } from "ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js";
// import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
const debug = process.env.NODE_ENV !== "production";
@@ -168,9 +168,9 @@ const store = createStore({
},
fetchOtherSocialIssues({ commit }) {
const url = `/api/1.0/person/social-work/social-issue.json`;
return makeFetch("GET", url)
return fetchResults(url)
.then((response) => {
commit("updateIssuesOther", response.results);
commit("updateIssuesOther", response); // Directly commit the array of issues
})
.catch((error) => {
throw error;