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

This commit is contained in:
Julien Fastré 2025-04-08 17:30:38 +02:00
commit f807d62dab
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
45 changed files with 622 additions and 210 deletions

View File

@ -1,6 +0,0 @@
kind: DX
body: Create an unique source of trust for translations
time: 2025-01-31T13:18:01.239211506+01:00
custom:
Issue: "333"
SchemaChange: No schema change

View File

@ -1,6 +0,0 @@
kind: Feature
body: Suggest all referrers within actions of the accompanying period when creating an activity
time: 2025-01-30T12:02:07.053587034+01:00
custom:
Issue: "349"
SchemaChange: No schema change

View File

@ -1,6 +0,0 @@
kind: Feature
body: Add possibility to export a csv with all social issues and social actions
time: 2025-01-30T12:04:59.754998541+01:00
custom:
Issue: "343"
SchemaChange: No schema change

View File

@ -1,6 +0,0 @@
kind: Feature
body: Restore document to previous kept version when a workflow is canceled
time: 2025-02-14T15:03:28.707250207+01:00
custom:
Issue: "360"
SchemaChange: No schema change

View File

@ -1,6 +0,0 @@
kind: Feature
body: Add a list of third parties from within the admin (csv download)
time: 2025-02-19T12:09:28.487991703+01:00
custom:
Issue: "341"
SchemaChange: No schema change

View File

@ -1,6 +0,0 @@
kind: Fixed
body: fix generation of document with accompanying period context, and list of activities and works
time: 2025-02-14T12:10:10.920355454+01:00
custom:
Issue: ""
SchemaChange: No schema change

6
.changes/v3.10.0.md Normal file
View File

@ -0,0 +1,6 @@
## v3.10.0 - 2025-03-17
### Feature
* ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form
### Fixed
* ([#362](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/362)) Fix Dependency Injection, which prevented to save the CalendarRange
* ([#368](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/368)) fix search query for user groups

3
.changes/v3.10.1.md Normal file
View File

@ -0,0 +1,3 @@
## v3.10.1 - 2025-03-17
### DX
* Remove yarn dependency to symfony/ux-translator, to ease the build process

3
.changes/v3.10.2.md Normal file
View File

@ -0,0 +1,3 @@
## v3.10.2 - 2025-03-17
### Fixed
* Replace a ts-expect-error with a ts-ignore

10
.changes/v3.9.0.md Normal file
View File

@ -0,0 +1,10 @@
## v3.9.0 - 2025-02-27
### Feature
* ([#349](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/349)) Suggest all referrers within actions of the accompanying period when creating an activity
* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add possibility to export a csv with all social issues and social actions
* ([#360](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/360)) Restore document to previous kept version when a workflow is canceled
* ([#341](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/341)) Add a list of third parties from within the admin (csv download)
### Fixed
* fix generation of document with accompanying period context, and list of activities and works
### DX
* ([#333](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/333)) Create an unique source of trust for translations

3
.changes/v3.9.1.md Normal file
View File

@ -0,0 +1,3 @@
## v3.9.1 - 2025-02-27
### Fixed
* Fix post/patch request with missing 'type' property for gender

3
.changes/v3.9.2.md Normal file
View File

@ -0,0 +1,3 @@
## v3.9.2 - 2025-02-27
### Fixed
* Use fetchResults method to fetch all social issues instead of only the first page

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ docker/rabbitmq/data
# in this development bundle, we want to ignore directories related to a real app # in this development bundle, we want to ignore directories related to a real app
assets/* assets/*
!assets/translator.ts !assets/translator.ts
!assets/ux-translator
migrations/* migrations/*
templates/* templates/*
translations/* translations/*

View File

@ -6,6 +6,40 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie). and is generated by [Changie](https://github.com/miniscruff/changie).
## v3.10.2 - 2025-03-17
### Fixed
* Replace a ts-expect-error with a ts-ignore
## v3.10.1 - 2025-03-17
### DX
* Remove yarn dependency to symfony/ux-translator, to ease the build process
## v3.10.0 - 2025-03-17
### Feature
* ([#363](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/363)) Display social actions grouped per social issue within activity form
### Fixed
* ([#362](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/362)) Fix Dependency Injection, which prevented to save the CalendarRange
* ([#368](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/368)) fix search query for user groups
## v3.9.2 - 2025-02-27
### Fixed
* Use fetchResults method to fetch all social issues instead of only the first page
## v3.9.1 - 2025-02-27
### Fixed
* Fix post/patch request with missing 'type' property for gender
## v3.9.0 - 2025-02-27
### Feature
* ([#349](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/349)) Suggest all referrers within actions of the accompanying period when creating an activity
* ([#343](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/343)) Add possibility to export a csv with all social issues and social actions
* ([#360](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/360)) Restore document to previous kept version when a workflow is canceled
* ([#341](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/341)) Add a list of third parties from within the admin (csv download)
### Fixed
* fix generation of document with accompanying period context, and list of activities and works
### DX
* ([#333](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/333)) Create an unique source of trust for translations
## v3.8.2 - 2025-02-10 ## v3.8.2 - 2025-02-10
### Fixed ### Fixed
* ([#358](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/358)) Remove "filter" button on list of documents in the workflow's "add attachement" modal * ([#358](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/358)) Remove "filter" button on list of documents in the workflow's "add attachement" modal

View File

@ -1,9 +1,7 @@
// @ts-ignore Cannot find module (when used within an app) import { trans, setLocale, setLocaleFallbacks } from "./ux-translator";
import { trans, getLocale, setLocale, setLocaleFallbacks } from "@symfony/ux-translator";
setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"}); setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"});
setLocale('fr'); setLocale('fr');
export { trans }; export { trans };
// @ts-ignore Cannot find module (when used within an app)
export * from '../var/translations'; export * from '../var/translations';

View File

@ -0,0 +1,3 @@
This directory import the symfony ux-translator files directly into chill-bundles.
This remove the yarn dependencies from the real package, which breaks our installation.

View File

@ -0,0 +1 @@
export declare function format(id: string, parameters: Record<string, string | number>, locale: string): string;

View File

@ -0,0 +1 @@
export declare function formatIntl(id: string, parameters: Record<string, string | number>, locale: string): string;

View File

@ -0,0 +1,27 @@
export type DomainType = string;
export type LocaleType = string;
export type TranslationsType = Record<DomainType, {
parameters: ParametersType;
}>;
export type NoParametersType = Record<string, never>;
export type ParametersType = Record<string, string | number | Date> | NoParametersType;
export type RemoveIntlIcuSuffix<T> = T extends `${infer U}+intl-icu` ? U : T;
export type DomainsOf<M> = M extends Message<infer Translations, LocaleType> ? keyof Translations : never;
export type LocaleOf<M> = M extends Message<TranslationsType, infer Locale> ? Locale : never;
export type ParametersOf<M, D extends DomainType> = M extends Message<infer Translations, LocaleType> ? Translations[D] extends {
parameters: infer Parameters;
} ? Parameters : never : never;
export interface Message<Translations extends TranslationsType, Locale extends LocaleType> {
id: string;
translations: {
[domain in DomainType]: {
[locale in Locale]: string;
};
};
}
export declare function setLocale(locale: LocaleType | null): void;
export declare function getLocale(): LocaleType;
export declare function throwWhenNotFound(enabled: boolean): void;
export declare function setLocaleFallbacks(localeFallbacks: Record<LocaleType, LocaleType>): void;
export declare function getLocaleFallbacks(): Record<LocaleType, LocaleType>;
export declare function trans<M extends Message<TranslationsType, LocaleType>, D extends DomainsOf<M>, P extends ParametersOf<M, D>>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>]): string;

View File

@ -0,0 +1 @@
export * from './translator';

View File

@ -0,0 +1,283 @@
import { IntlMessageFormat } from 'intl-messageformat';
function strtr(string, replacePairs) {
const regex = Object.entries(replacePairs).map(([from]) => {
return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, '\\$1');
});
if (regex.length === 0) {
return string;
}
return string.replace(new RegExp(regex.join('|'), 'g'), (matched) => replacePairs[matched].toString());
}
function format(id, parameters, locale) {
if (null === id || '' === id) {
return '';
}
if (typeof parameters['%count%'] === 'undefined' || Number.isNaN(parameters['%count%'])) {
return strtr(id, parameters);
}
const number = Number(parameters['%count%']);
let parts = [];
if (/^\|+$/.test(id)) {
parts = id.split('|');
}
else {
parts = id.match(/(?:\|\||[^|])+/g) || [];
}
const intervalRegex = /^(?<interval>({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?<left_delimiter>[[\]])\s*(?<left>-Inf|-?\d+(\.\d+)?)\s*,\s*(?<right>\+?Inf|-?\d+(\.\d+)?)\s*(?<right_delimiter>[[\]]))\s*(?<message>.*?)$/s;
const standardRules = [];
for (let part of parts) {
part = part.trim().replace(/\|\|/g, '|');
const matches = part.match(intervalRegex);
if (matches) {
const matchGroups = matches.groups || {};
if (matches[2]) {
for (const n of matches[3].split(',')) {
if (number === Number(n)) {
return strtr(matchGroups.message, parameters);
}
}
}
else {
const leftNumber = '-Inf' === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left);
const rightNumber = ['Inf', '+Inf'].includes(matchGroups.right)
? Number.POSITIVE_INFINITY
: Number(matchGroups.right);
if (('[' === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) &&
(']' === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) {
return strtr(matchGroups.message, parameters);
}
}
}
else {
const ruleMatch = part.match(/^\w+:\s*(.*?)$/);
standardRules.push(ruleMatch ? ruleMatch[1] : part);
}
}
const position = getPluralizationRule(number, locale);
if (typeof standardRules[position] === 'undefined') {
if (1 === parts.length && typeof standardRules[0] !== 'undefined') {
return strtr(standardRules[0], parameters);
}
throw new Error(`Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").`);
}
return strtr(standardRules[position], parameters);
}
function getPluralizationRule(number, locale) {
number = Math.abs(number);
let _locale = locale;
if (locale === 'pt_BR' || locale === 'en_US_POSIX') {
return 0;
}
_locale = _locale.length > 3 ? _locale.substring(0, _locale.indexOf('_')) : _locale;
switch (_locale) {
case 'af':
case 'bn':
case 'bg':
case 'ca':
case 'da':
case 'de':
case 'el':
case 'en':
case 'en_US_POSIX':
case 'eo':
case 'es':
case 'et':
case 'eu':
case 'fa':
case 'fi':
case 'fo':
case 'fur':
case 'fy':
case 'gl':
case 'gu':
case 'ha':
case 'he':
case 'hu':
case 'is':
case 'it':
case 'ku':
case 'lb':
case 'ml':
case 'mn':
case 'mr':
case 'nah':
case 'nb':
case 'ne':
case 'nl':
case 'nn':
case 'no':
case 'oc':
case 'om':
case 'or':
case 'pa':
case 'pap':
case 'ps':
case 'pt':
case 'so':
case 'sq':
case 'sv':
case 'sw':
case 'ta':
case 'te':
case 'tk':
case 'ur':
case 'zu':
return 1 === number ? 0 : 1;
case 'am':
case 'bh':
case 'fil':
case 'fr':
case 'gun':
case 'hi':
case 'hy':
case 'ln':
case 'mg':
case 'nso':
case 'pt_BR':
case 'ti':
case 'wa':
return number < 2 ? 0 : 1;
case 'be':
case 'bs':
case 'hr':
case 'ru':
case 'sh':
case 'sr':
case 'uk':
return 1 === number % 10 && 11 !== number % 100
? 0
: number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)
? 1
: 2;
case 'cs':
case 'sk':
return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2;
case 'ga':
return 1 === number ? 0 : 2 === number ? 1 : 2;
case 'lt':
return 1 === number % 10 && 11 !== number % 100
? 0
: number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20)
? 1
: 2;
case 'sl':
return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3;
case 'mk':
return 1 === number % 10 ? 0 : 1;
case 'mt':
return 1 === number
? 0
: 0 === number || (number % 100 > 1 && number % 100 < 11)
? 1
: number % 100 > 10 && number % 100 < 20
? 2
: 3;
case 'lv':
return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2;
case 'pl':
return 1 === number
? 0
: number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14)
? 1
: 2;
case 'cy':
return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3;
case 'ro':
return 1 === number ? 0 : 0 === number || (number % 100 > 0 && number % 100 < 20) ? 1 : 2;
case 'ar':
return 0 === number
? 0
: 1 === number
? 1
: 2 === number
? 2
: number % 100 >= 3 && number % 100 <= 10
? 3
: number % 100 >= 11 && number % 100 <= 99
? 4
: 5;
default:
return 0;
}
}
function formatIntl(id, parameters, locale) {
if (id === '') {
return '';
}
const intlMessage = new IntlMessageFormat(id, [locale.replace('_', '-')], undefined, { ignoreTag: true });
parameters = { ...parameters };
Object.entries(parameters).forEach(([key, value]) => {
if (key.includes('%') || key.includes('{')) {
delete parameters[key];
parameters[key.replace(/[%{} ]/g, '').trim()] = value;
}
});
return intlMessage.format(parameters);
}
let _locale = null;
let _localeFallbacks = {};
let _throwWhenNotFound = false;
function setLocale(locale) {
_locale = locale;
}
function getLocale() {
return (_locale ||
document.documentElement.getAttribute('data-symfony-ux-translator-locale') ||
(document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) ||
'en');
}
function throwWhenNotFound(enabled) {
_throwWhenNotFound = enabled;
}
function setLocaleFallbacks(localeFallbacks) {
_localeFallbacks = localeFallbacks;
}
function getLocaleFallbacks() {
return _localeFallbacks;
}
function trans(message, parameters = {}, domain = 'messages', locale = null) {
if (typeof domain === 'undefined') {
domain = 'messages';
}
if (typeof locale === 'undefined' || null === locale) {
locale = getLocale();
}
if (typeof message.translations === 'undefined') {
return message.id;
}
const localesFallbacks = getLocaleFallbacks();
const translationsIntl = message.translations[`${domain}+intl-icu`];
if (typeof translationsIntl !== 'undefined') {
while (typeof translationsIntl[locale] === 'undefined') {
locale = localesFallbacks[locale];
if (!locale) {
break;
}
}
if (locale) {
return formatIntl(translationsIntl[locale], parameters, locale);
}
}
const translations = message.translations[domain];
if (typeof translations !== 'undefined') {
while (typeof translations[locale] === 'undefined') {
locale = localesFallbacks[locale];
if (!locale) {
break;
}
}
if (locale) {
return format(translations[locale], parameters, locale);
}
}
if (_throwWhenNotFound) {
throw new Error(`No translation message found with id "${message.id}".`);
}
return message.id;
}
export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans };

1
assets/ux-translator/dist/utils.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export declare function strtr(string: string, replacePairs: Record<string, string | number>): string;

View File

@ -0,0 +1,34 @@
{
"name": "@symfony/ux-translator",
"description": "Symfony Translator for JavaScript",
"license": "MIT",
"version": "1.0.0",
"main": "dist/translator_controller.js",
"types": "dist/translator_controller.d.ts",
"scripts": {
"build": "node ../../../bin/build_package.js .",
"watch": "node ../../../bin/build_package.js . --watch",
"test": "../../../bin/test_package.sh .",
"check": "biome check",
"ci": "biome ci"
},
"symfony": {
"importmap": {
"intl-messageformat": "^10.5.11",
"@symfony/ux-translator": "path:%PACKAGE%/dist/translator_controller.js",
"@app/translations": "path:var/translations/index.js",
"@app/translations/configuration": "path:var/translations/configuration.js"
}
},
"peerDependencies": {
"intl-messageformat": "^10.5.11"
},
"peerDependenciesMeta": {
"intl-messageformat": {
"optional": false
}
},
"devDependencies": {
"intl-messageformat": "^10.5.11"
}
}

View File

@ -59,6 +59,7 @@
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"dropzone": "^5.7.6", "dropzone": "^5.7.6",
"es6-promise": "^4.2.8", "es6-promise": "^4.2.8",
"intl-messageformat": "^10.5.11",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"marked": "^12.0.2", "marked": "^12.0.2",
"masonry-layout": "^4.2.2", "masonry-layout": "^4.2.2",

View File

@ -68,14 +68,23 @@
socialActionsSelected.length) socialActionsSelected.length)
" "
> >
<check-social-action <div
v-for="action in socialActionsList" id="actionsList"
:key="action.id" v-for="group in socialActionsList"
:action="action" :key="group.issue"
:selection="socialActionsSelected"
@updateSelected="updateActionsSelected"
> >
</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> </template>
<span <span
@ -149,53 +158,44 @@ export default {
}, },
}, },
mounted() { mounted() {
/* Load others issues in multiselect /* Load other issues in multiselect */
*/
this.issueIsLoading = true; this.issueIsLoading = true;
this.actionAreLoaded = false; 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) getSocialIssues().then((response) => {
*/ /* Add issues to the store */
this.socialIssuesSelected.forEach((issue) => { this.$store.commit("updateIssuesOther", response);
if (
this.socialIssuesList.filter(
(i) => i.id === issue.id,
).length !== 1
) {
this.$store.commit("addIssueInList", issue);
}
}, this);
/* Remove from multiselect the issues that are not yet in checkbox list /* Add in list the issues already associated (if not yet listed) */
*/ this.socialIssuesSelected.forEach((issue) => {
this.socialIssuesList.forEach((issue) => { if (
this.$store.commit("removeIssueInOther", issue); this.socialIssuesList.filter((i) => i.id === issue.id)
}, this); .length !== 1
) {
this.$store.commit("addIssueInList", issue);
}
});
/* Filter issues /* Remove from multiselect the issues that are not yet in the checkbox list */
*/ this.socialIssuesList.forEach((issue) => {
this.$store.commit("filterList", "issues"); this.$store.commit("removeIssueInOther", issue);
});
/* Add in list the actions already associated (if not yet listed) /* Filter issues */
*/ this.$store.commit("filterList", "issues");
this.socialActionsSelected.forEach((action) => {
this.$store.commit("addActionInList", action);
}, this);
/* Filter issues /* Add in list the actions already associated (if not yet listed) */
*/ this.socialActionsSelected.forEach((action) => {
this.$store.commit("filterList", "actions"); this.$store.commit("addActionInList", action);
});
this.issueIsLoading = false; /* Filter actions */
this.actionAreLoaded = true; this.$store.commit("filterList", "actions");
this.updateActionsList();
resolve(); this.issueIsLoading = false;
}), this.actionAreLoaded = true;
); this.updateActionsList();
});
}, },
methods: { methods: {
/* When choosing an issue in multiselect, add it in checkboxes (as selected), /* When choosing an issue in multiselect, add it in checkboxes (as selected),
@ -258,7 +258,23 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "ChillMainAssets/module/bootstrap/shared";
@import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables";
span.multiselect__single { span.multiselect__single {
display: none !important; 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> </style>

View File

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

View File

@ -124,9 +124,19 @@ const store = createStore({
); );
}, },
socialActionsListSorted(state) { socialActionsListSorted(state) {
return [...state.socialActionsList].sort( return [...state.socialActionsList]
(a, b) => a.ordering - b.ordering, .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: { mutations: {

View File

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

View File

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

View File

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

View File

@ -63,7 +63,6 @@ abstract class AbstractCRUDController extends AbstractController
parent::getSubscribedServices(), parent::getSubscribedServices(),
[ [
'chill_main.paginator_factory' => PaginatorFactory::class, 'chill_main.paginator_factory' => PaginatorFactory::class,
ManagerRegistry::class => ManagerRegistry::class,
'translator' => TranslatorInterface::class, 'translator' => TranslatorInterface::class,
AuthorizationHelper::class => AuthorizationHelper::class, AuthorizationHelper::class => AuthorizationHelper::class,
EventDispatcherInterface::class => EventDispatcherInterface::class, EventDispatcherInterface::class => EventDispatcherInterface::class,
@ -213,7 +212,7 @@ abstract class AbstractCRUDController extends AbstractController
protected function getManagerRegistry(): ManagerRegistry 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 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(' ->setWhereClauses('
ug.active AND ( ug.active AND (
SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) > 0.15 SIMILARITY(LOWER(UNACCENT(?)), ug.label->>?) > 0.15
OR ug.label->>? LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\') OR LOWER(UNACCENT(ug.label->>?)) LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\')
', [$pattern, $lang, $pattern, $lang]); ', [$pattern, $lang, $lang, $pattern]);
return $query; return $query;
} }

View File

@ -22,12 +22,12 @@ function loadDynamicPicker(element) {
? JSON.parse(input.value) ? JSON.parse(input.value)
: input.value === "[]" || input.value === "" : input.value === "[]" || input.value === ""
? null ? null
: [JSON.parse(input.value)]; : [JSON.parse(input.value)],
(suggested = JSON.parse(el.dataset.suggested)), suggested = JSON.parse(el.dataset.suggested),
(as_id = parseInt(el.dataset.asId) === 1), as_id = parseInt(el.dataset.asId) === 1,
(submit_on_adding_new_entity = submit_on_adding_new_entity =
parseInt(el.dataset.submitOnAddingNewEntity) === 1); parseInt(el.dataset.submitOnAddingNewEntity) === 1,
label = el.dataset.label; label = el.dataset.label;
if (!isMultiple) { if (!isMultiple) {
if (input.value === "[]") { 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); loadDynamicPicker(document);
}); });

View File

@ -45,6 +45,10 @@ const onPickGenericDoc = ({
}) => { }) => {
emit("pickGenericDoc", { genericDoc }); emit("pickGenericDoc", { genericDoc });
}; };
const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => {
emit("removeAttachment", payload);
};
</script> </script>
<template> <template>
@ -56,7 +60,7 @@ const onPickGenericDoc = ({
></pick-generic-doc-modal> ></pick-generic-doc-modal>
<attachment-list <attachment-list
:attachments="props.attachments" :attachments="props.attachments"
@removeAttachment="(payload) => emit('removeAttachment', payload)" @removeAttachment="onRemoveAttachment"
></attachment-list> ></attachment-list>
<ul class="record_actions"> <ul class="record_actions">
<li> <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[]>(() => { const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
if (false === loaded.value) { if (false === loaded.value) {
return []; return [];
@ -245,10 +253,8 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
:accompanying-period-id="accompanyingPeriodId" :accompanying-period-id="accompanyingPeriodId"
:genericDoc="g" :genericDoc="g"
:is-picked="isPicked(g)" :is-picked="isPicked(g)"
@pickGenericDoc="(payload) => emit('pickGenericDoc', payload)" @pickGenericDoc="onPickDocument"
@removeGenericDoc=" @removeGenericDoc="onRemoveGenericDoc"
(payload) => emit('removeGenericDoc', payload)
"
></pick-generic-doc-item> ></pick-generic-doc-item>
</div> </div>
<div v-else class="text-center chill-no-data-statement"> <div v-else class="text-center chill-no-data-statement">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -943,6 +943,16 @@ paths:
description: "ok" description: "ok"
401: 401:
description: "Unauthorized" 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: /1.0/main/user-job.json:
get: get:
tags: tags:

View File

@ -33,18 +33,7 @@ const getUserJobs = () => fetchResults("/api/1.0/main/user-job.json");
const getSocialIssues = () => { const getSocialIssues = () => {
const url = `/api/1.0/person/social-work/social-issue.json`; const url = `/api/1.0/person/social-work/social-issue.json`;
return fetch(url).then((response) => { return fetchResults(url);
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,
};
});
}; };
const whoami = () => { const whoami = () => {

View File

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

View File

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