mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 17:28:23 +00:00 
			
		
		
		
	Compare commits
	
		
			123 Commits
		
	
	
		
			v3.8.2
			...
			321-text-e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b5c9e65986 | |||
| 8b2af35e97 | |||
| dc44c46667 | |||
| ba571c1a69 | |||
| 6a364705f2 | |||
| b6d454691a | |||
| 6d7a6932a9 | |||
|  | 2faf194b15 | ||
| f207599d86 | |||
| b0959f8cc5 | |||
| 4c5dee5f0a | |||
| f6c98aa0d5 | |||
| 6d13d184d5 | |||
| af36eccfaf | |||
| 483a20a43f | |||
| 6d8e2ad825 | |||
| 86388a63a8 | |||
|  | 5ea55ebfe5 | ||
| f97dc8f931 | |||
|  | a9c3aab528 | ||
| 1181377bd6 | |||
|  | 2275b7c560 | ||
| 4a8d298ae5 | |||
| 3e7f03d331 | |||
| b830952b9e | |||
| ad17313c61 | |||
| 620515ad15 | |||
| 50c377ee22 | |||
| cc7e7a90ee | |||
| 1d4ef19051 | |||
| 8337a724d1 | |||
| 8ca377d5d4 | |||
| 224e0bae43 | |||
| 3aa4fac80d | |||
|  | a7517eb647 | ||
| e278e636e0 | |||
|  | 40e373a9c7 | ||
| 1c1f418b18 | |||
| bf0e14b43a | |||
| 203a098054 | |||
| d58acff541 | |||
| 5858e05a42 | |||
| b9b4fafe14 | |||
| fe6949ea26 | |||
| 8a444a12f4 | |||
| 8b7b5ceed7 | |||
| b0e826d05a | |||
| 6fb9c3af3f | |||
| 7f101ba616 | |||
| cea82fac10 | |||
| 1344fc33e1 | |||
| c8e09a28e6 | |||
| c52d4b2a0e | |||
| 7f326d5441 | |||
| 2840c06476 | |||
| 7ddf84ea5a | |||
| f202625ea8 | |||
| 7a9168fcdb | |||
| 40eb71f95a | |||
| 84b7cc8145 | |||
| 08af530726 | |||
| 03b2496817 | |||
| f638ce71fd | |||
| b39997f00a | |||
| 38b21a2159 | |||
| 17db571244 | |||
| 8c8c16c1a1 | |||
| 046d3ec9f1 | |||
| 596833f1a5 | |||
| 66e4bab558 | |||
| d1c9926bb1 | |||
| a71d066765 | |||
| 217ac7b9e7 | |||
| be901822bc | |||
| 2dcce7b826 | |||
| 7ed10efcd1 | |||
| 350661a4fa | |||
| 08207b656a | |||
| 8de63de6d6 | |||
| 51804b10c0 | |||
| 02f555efae | |||
| d2fcb6945b | |||
| dcd1777a70 | |||
| a6eb28175a | |||
| c89e3785ef | |||
| 9f17ec4841 | |||
| b277a7749a | |||
| c8b6b6e33a | |||
| 10eaebf610 | |||
| 7d78512823 | |||
| 0a34f9086f | |||
| 739e0b1692 | |||
| 8db8f5fdf5 | |||
| d0cd4792d6 | |||
| 6d196ead94 | |||
| 4047d5fd5b | |||
| 9aac80d834 | |||
| 7560dc57c6 | |||
| 10314845f6 | |||
| 9b84bc4d69 | |||
| a2fcf039be | |||
| b4d887a372 | |||
| 0aaa7122da | |||
| 1bc7f85874 | |||
| 1d2fd000aa | |||
| fc32f9eca9 | |||
|  | 03717a1a87 | ||
|  | 02c524dd79 | ||
| 506df432b0 | |||
| c32c18b0e2 | |||
| 321d569ee9 | |||
| cd40eb3932 | |||
| f0f2531fa3 | |||
| 183a220e7b | |||
| 9df127a82c | |||
| 04a1412562 | |||
| 3aef0a185e | |||
| 578bce31b9 | |||
|  | bc7f0907ab | ||
|  | fbdc0d32f0 | ||
|  | 5f31473c90 | ||
|  | 98cf167040 | ||
|  | 6c37d798bf | 
							
								
								
									
										6
									
								
								.changes/unreleased/DX-20250430-144550.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/DX-20250430-144550.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: DX | ||||
| body: Remove dead code for wopi-link module | ||||
| time: 2025-04-30T14:45:50.406111606+02:00 | ||||
| custom: | ||||
|     Issue: "352" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										7
									
								
								.changes/unreleased/Feature-20250424-142211.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Feature-20250424-142211.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| kind: Feature | ||||
| body: Add the document file name to the document title when a user upload a document, | ||||
|   unless there is already a document title. | ||||
| time: 2025-04-24T14:22:11.800975422+02:00 | ||||
| custom: | ||||
|   Issue: "377" | ||||
|   SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20250520-095628.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20250520-095628.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Feature | ||||
| body: Add desactivation date for social action and issue csv export | ||||
| time: 2025-05-20T09:56:28.108941934+02:00 | ||||
| custom: | ||||
|     Issue: "" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										7
									
								
								.changes/unreleased/Fixed-20250424-133943.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Fixed-20250424-133943.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| kind: Fixed | ||||
| body: trying to prevent bug of typeerror in doc-history + improved display of document | ||||
|   history | ||||
| time: 2025-04-24T13:39:43.878468232+02:00 | ||||
| custom: | ||||
|   Issue: "376" | ||||
|   SchemaChange: No schema change | ||||
							
								
								
									
										7
									
								
								.changes/unreleased/Fixed-20250424-163746.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.changes/unreleased/Fixed-20250424-163746.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| kind: Fixed | ||||
| body: Display previous participation in acc course work even if the person has left | ||||
|   the acc course | ||||
| time: 2025-04-24T16:37:46.970203594+02:00 | ||||
| custom: | ||||
|   Issue: "381" | ||||
|   SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250505-102715.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250505-102715.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Fixed | ||||
| body: Fix display of text in calendar events | ||||
| time: 2025-05-05T10:27:15.461493066+02:00 | ||||
| custom: | ||||
|     Issue: "372" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/Fixed-20250514-145339.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Fixed-20250514-145339.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: Fixed | ||||
| body: Add missing translation for user_group.no_user_groups | ||||
| time: 2025-05-14T14:53:39.53927329+02:00 | ||||
| custom: | ||||
|     Issue: "" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/unreleased/UX-20250423-172624.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/UX-20250423-172624.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| kind: UX | ||||
| body: Remove default filter in_progress for the page 'my tasks'; Allows for new tasks to be displayed upon opening of the page | ||||
| time: 2025-04-23T17:26:24.45777387+02:00 | ||||
| custom: | ||||
|     Issue: "374" | ||||
|     SchemaChange: No schema change | ||||
							
								
								
									
										6
									
								
								.changes/v3.10.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/v3.10.0.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								.changes/v3.10.1.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								.changes/v3.10.2.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ## v3.10.2 - 2025-03-17 | ||||
| ### Fixed | ||||
| * Replace a ts-expect-error with a ts-ignore    | ||||
							
								
								
									
										3
									
								
								.changes/v3.10.3.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.changes/v3.10.3.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ## v3.10.3 - 2025-03-18 | ||||
| ### DX | ||||
| * Eslint fixes    | ||||
							
								
								
									
										19
									
								
								.changes/v3.11.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								.changes/v3.11.0.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| ## v3.11.0 - 2025-04-17 | ||||
| ### Feature | ||||
| * ([#365](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/365)) Add counters of actions and activities, with 2 boxes to (1) show the number of active actions on total actions and (2) show the number of activities in a accompanying period, and pills in menus for showing the number of active actions and the number of activities. | ||||
| * ([#364](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/364)) Added a second phone number "telephone2" to the thirdParty entity. Adapted twig templates and vuejs apps to handle this phone number | ||||
|  | ||||
|   **Schema Change**: Add columns or tables | ||||
| * Signature: add a button to go directly to the signature zone, even if there is only one | ||||
| ### Fixed | ||||
| * Fixed wrong translations in the on-the-fly for creation of thirdParty | ||||
| * Fixed update of phone number in on-the-fly edition of thirdParty | ||||
| * Fixed closing of modal when editing thirdParty in accompanying course works | ||||
| * Shorten the delay between two execution of AccompanyingPeriodStepChangeCronjob, to ensure at least one execution in a day | ||||
| * ([#102](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/102)) Fix display of title in document list | ||||
| * When cleaning the old stored object versions, do not throw an error if the stored object is not found on disk | ||||
| * Add consistent log prefix and key to logs when stale workflows are automatically canceled | ||||
| * ([#380](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/380)) Remove the "not null" validation constraint on recently added properties on HouseholdComposition | ||||
|  | ||||
| ### DX | ||||
| * Add new chill-col style for displaying title and aside in a flex table | ||||
							
								
								
									
										10
									
								
								.changes/v3.9.0.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.changes/v3.9.0.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								.changes/v3.9.1.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										3
									
								
								.changes/v3.9.2.md
									
									
									
									
									
										Normal 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    | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -12,6 +12,8 @@ docker/rabbitmq/data | ||||
|  | ||||
| # in this development bundle, we want to ignore directories related to a real app | ||||
| assets/* | ||||
| !assets/translator.ts | ||||
| !assets/ux-translator | ||||
| migrations/* | ||||
| templates/* | ||||
| translations/* | ||||
|   | ||||
| @@ -113,7 +113,7 @@ lint: | ||||
|         - export PATH="./node_modules/.bin:$PATH" | ||||
|     script: | ||||
|         - yarn install --ignore-optional | ||||
|         - npx eslint-baseline "**/*.{js,vue}" | ||||
|         - npx eslint-baseline "src/**/*.{js,ts,vue}" | ||||
|     cache: | ||||
|         paths: | ||||
|             - node_modules/ | ||||
|   | ||||
							
								
								
									
										38
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -6,6 +6,44 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), | ||||
| and is generated by [Changie](https://github.com/miniscruff/changie). | ||||
|  | ||||
|  | ||||
| ## v3.10.3 - 2025-03-18 | ||||
| ### DX | ||||
| * Eslint fixes    | ||||
|  | ||||
| ## 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 | ||||
| ### 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    | ||||
|   | ||||
							
								
								
									
										7
									
								
								assets/translator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								assets/translator.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { trans, setLocale, setLocaleFallbacks } from "./ux-translator"; | ||||
|  | ||||
| setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"}); | ||||
| setLocale('fr'); | ||||
|  | ||||
| export { trans }; | ||||
| export * from '../var/translations'; | ||||
							
								
								
									
										3
									
								
								assets/ux-translator/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								assets/ux-translator/README.md
									
									
									
									
									
										Normal 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. | ||||
							
								
								
									
										1
									
								
								assets/ux-translator/dist/formatters/formatter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/ux-translator/dist/formatters/formatter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export declare function format(id: string, parameters: Record<string, string | number>, locale: string): string; | ||||
							
								
								
									
										1
									
								
								assets/ux-translator/dist/formatters/intl-formatter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/ux-translator/dist/formatters/intl-formatter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export declare function formatIntl(id: string, parameters: Record<string, string | number>, locale: string): string; | ||||
							
								
								
									
										27
									
								
								assets/ux-translator/dist/translator.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								assets/ux-translator/dist/translator.d.ts
									
									
									
									
										vendored
									
									
										Normal 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; | ||||
							
								
								
									
										1
									
								
								assets/ux-translator/dist/translator_controller.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assets/ux-translator/dist/translator_controller.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './translator'; | ||||
							
								
								
									
										283
									
								
								assets/ux-translator/dist/translator_controller.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								assets/ux-translator/dist/translator_controller.js
									
									
									
									
										vendored
									
									
										Normal 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
									
								
							
							
						
						
									
										1
									
								
								assets/ux-translator/dist/utils.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export declare function strtr(string: string, replacePairs: Record<string, string | number>): string; | ||||
							
								
								
									
										34
									
								
								assets/ux-translator/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								assets/ux-translator/package.json
									
									
									
									
									
										Normal 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" | ||||
|     } | ||||
| } | ||||
| @@ -75,6 +75,7 @@ | ||||
|         "symfony/templating": "^5.4", | ||||
|         "symfony/translation": "^5.4", | ||||
|         "symfony/twig-bundle": "^5.4", | ||||
|         "symfony/ux-translator": "^2.22", | ||||
|         "symfony/validator": "^5.4", | ||||
|         "symfony/webpack-encore-bundle": "^1.11", | ||||
|         "symfony/workflow": "^5.4", | ||||
|   | ||||
| @@ -36,4 +36,5 @@ return [ | ||||
|     Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true], | ||||
|     Chill\WopiBundle\ChillWopiBundle::class => ['all' => true], | ||||
|     Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], | ||||
|     Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true], | ||||
| ]; | ||||
|   | ||||
							
								
								
									
										3
									
								
								config/packages/ux_translator.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								config/packages/ux_translator.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ux_translator: | ||||
|     # The directory where the JavaScript translations are dumped | ||||
|     dump_directory: '%kernel.project_dir%/var/translations' | ||||
| @@ -220,6 +220,7 @@ framework: | ||||
|                         - attenteModification | ||||
|                         - attenteMiseEnForme | ||||
|                         - attenteValidationMiseEnForme | ||||
|                         - attenteSignature | ||||
|                         - attenteVisa | ||||
|                         - postSignature | ||||
|                         - attenteTraitement | ||||
|   | ||||
| @@ -29,8 +29,7 @@ We strongly encourage you to initialize a git repository at this step, to track | ||||
|    # add the flex endpoints required for custom recipes | ||||
|    cat <<< "$(jq '.extra.symfony += {"endpoint": ["flex://defaults", "https://gitlab.com/api/v4/projects/57371968/repository/files/index.json/raw?ref=main"]}' composer.json)" > composer.json | ||||
|    # install chill and some dependencies | ||||
|    # TODO fix the suffix "alpha1" and replace by ^3.0.0 when version 3.0.0 will be released | ||||
|    symfony composer require chill-project/chill-bundles v3.0.0-RC3 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev | ||||
|    symfony composer require chill-project/chill-bundles ^3.7.1 champs-libres/wopi-lib dev-master@dev champs-libres/wopi-bundle dev-master@dev symfony/amqp-messenger | ||||
|  | ||||
| We encourage you to accept the inclusion of the "Docker configuration from recipes": this is the documented way to run the database. | ||||
| You must also accept to configure recipes from the contrib repository, unless you want to configure the bundles manually). | ||||
| @@ -48,7 +47,7 @@ You must also accept to configure recipes from the contrib repository, unless yo | ||||
|  | ||||
|    If you encounter this error during assets compilation (:code:`yarn run encore production`) (repeated multiple times): | ||||
|  | ||||
|    .. code-block:: txt | ||||
|    .. code-block:: | ||||
|  | ||||
|       [tsl] ERROR in /tmp/chill/v1/public/bundles/chillcalendar/types.ts(2,65) | ||||
|             TS2307: Cannot find module '../../../ChillMainBundle/Resources/public/types' or its corresponding type declarations. | ||||
| @@ -74,14 +73,22 @@ or in the :code:`.env.local` file, which should not be committed to the git repo | ||||
| You do not need to set variables for the smtp server, redis server and relatorio server, as they are generated automatically | ||||
| by the symfony server, from the docker compose services. | ||||
|  | ||||
| The only required variable is the :code:`ADMIN_PASSWORD`. You can generate a hashed and salted admin password using the command | ||||
| :code:`symfony console security:hash-password <your password> 'Symfony\Component\Security\Core\User\User'`. Then, | ||||
| The required variables are: | ||||
|  | ||||
| - the :code:`ADMIN_PASSWORD`; | ||||
| - the :code:`OVHCLOUD_DSN` variable; | ||||
|  | ||||
| :code:`ADMIN_PASSWORD` | ||||
| ^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| You can generate a hashed and salted admin password using the command | ||||
| :code:`symfony console security:hash-password <your password> 'Symfony\Component\Security\Core\User\User'`.Then, | ||||
| you can either: | ||||
|  | ||||
| - add this password to the :code:`.env.local` file, you must escape the character :code:`$`: if the generated password | ||||
|   is :code:`$2y$13$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm`, your :code:`.env.local` file will be: | ||||
|  | ||||
|   .. code-block:: env | ||||
|   .. code-block:: bash | ||||
|  | ||||
|      ADMIN_PASSWORD=\$2y\$13\$iyvJLuT4YEa6iWXyQV4/N.hNHpNG8kXlYDkkt5MkYy4FXcSwYAwmm | ||||
|      # note: if you copy-paste the line above, the password will be "admin". | ||||
| @@ -89,12 +96,24 @@ you can either: | ||||
| - add the generated password to the secrets manager (**note**: you must add the generated hashed password to the secrets env, | ||||
|   not the password in clear text). | ||||
|  | ||||
| - set up the jwt authentication bundle | ||||
| :code:`OVHCLOUD_DSN` and sending SMS messages | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
| This is a temporary dependency, for ensuring compatibility for previous behaviour. | ||||
|  | ||||
| You can set it to :code:`null://null` if you do not plan to use sending SMS. | ||||
|  | ||||
| .. code-block:: bash | ||||
|  | ||||
|    OVHCLOUD_DSN=null://null | ||||
|  | ||||
| If you plan to do it, you can configure the notifier component `as described in the symfony documentation <https://symfony.com/doc/current/notifier.html#notifier-sms-channel>`_. | ||||
|  | ||||
|  | ||||
| Some environment variables are available for the JWT authentication bundle in the :code:`.env` file. | ||||
|  | ||||
| Prepare migrations and other tools | ||||
| ---------------------------------- | ||||
| Prepare database, messenger queue, and other configuration | ||||
| ---------------------------------------------------------- | ||||
|  | ||||
| To continue the installation process, you will have to run migrations: | ||||
|  | ||||
| @@ -109,17 +128,22 @@ To continue the installation process, you will have to run migrations: | ||||
|    symfony console messenger:setup-transports | ||||
|    # prepare some views | ||||
|    symfony console chill:db:sync-views | ||||
|    # load languages data | ||||
|    symfony console chill:main:languages:populate | ||||
|    # generate jwt token, required for some api features (webdav access, ...) | ||||
|    symfony console lexik:jwt:generate-keypair | ||||
|  | ||||
| .. warning:: | ||||
| .. note:: | ||||
|  | ||||
|    If you encounter an error while running :code:`symfony console messenger:setup-transports`, you can set up the messenger | ||||
|    transport to redis, by adding this in the :code:`.env.local` or :code:`.env` file: | ||||
|    If you encounter this error: | ||||
|  | ||||
|    .. code-block:: | ||||
|  | ||||
|      No transport supports the given Messenger DSN. | ||||
|  | ||||
|    Please check that you installed the package `symfony/amqp-messenger`. | ||||
|  | ||||
|    .. code-block:: env | ||||
|  | ||||
|       MESSENGER_TRANSPORT_DSN=redis://${REDIS_HOST}:${REDIS_PORT}/messages | ||||
|  | ||||
| Start your web server locally | ||||
| ----------------------------- | ||||
|   | ||||
| @@ -41,16 +41,18 @@ Postal code are loaded from this database. There is no need to load postal codes | ||||
| The data are prepared for Chill (`See this repository <https://gitea.champs-libres.be/Chill-project/belgian-bestaddresses-transform/releases>`_). | ||||
| One can select postal code by his first number (:code:`1xxx` for postal codes from 1000 to 1999), or a limited list for development purpose. | ||||
|  | ||||
| The command expects a language code as first argument. | ||||
|  | ||||
| .. code-block:: bash | ||||
|  | ||||
|    # load postal code from 1000 to 3999: | ||||
|    bin/console chill:main:address-ref-from-best-addresse 1xxx 2xxx 3xxx | ||||
|    bin/console chill:main:address-ref-from-best-addresse fr 1xxx 2xxx 3xxx | ||||
|  | ||||
|    # load only an extract (for dev purposes) | ||||
|    bin/console chill:main:address-ref-from-best-addresse extract | ||||
|    bin/console chill:main:address-ref-from-best-addresse fr extract | ||||
|  | ||||
|    # load full addresses (discouraged) | ||||
|    bin/console chill:main:address-ref-from-best-addresse full | ||||
|    bin/console chill:main:address-ref-from-best-addresse fr full | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|   | ||||
| @@ -34,6 +34,7 @@ export default ts.config( | ||||
|             // override/add rules settings here, such as: | ||||
|             "vue/multi-word-component-names": "off", | ||||
|             "@typescript-eslint/no-require-imports": "off", | ||||
|             "@typescript-eslint/ban-ts-comment": "off" | ||||
|         }, | ||||
|     }, | ||||
| ); | ||||
|   | ||||
							
								
								
									
										17
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								package.json
									
									
									
									
									
								
							| @@ -6,15 +6,12 @@ | ||||
|     "@apidevtools/swagger-cli": "^4.0.4", | ||||
|     "@babel/core": "^7.20.5", | ||||
|     "@babel/preset-env": "^7.20.2", | ||||
|     "@ckeditor/ckeditor5-build-classic": "^41.4.2", | ||||
|     "@ckeditor/ckeditor5-dev-translations": "^40.2.0", | ||||
|     "@ckeditor/ckeditor5-dev-utils": "^40.2.0", | ||||
|     "@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13", | ||||
|     "@ckeditor/ckeditor5-markdown-gfm": "^41.4.2", | ||||
|     "@ckeditor/ckeditor5-theme-lark": "^41.4.2", | ||||
|     "@ckeditor/ckeditor5-vue": "^5.1.0", | ||||
|     "@ckeditor/ckeditor5-vue": "^7.3.0", | ||||
|     "@eslint/js": "^9.14.0", | ||||
|     "@hotwired/stimulus": "^3.0.0", | ||||
|     "@luminateone/eslint-baseline": "^1.0.9", | ||||
|     "@symfony/stimulus-bridge": "^3.2.0", | ||||
|     "@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets", | ||||
|     "@symfony/webpack-encore": "^4.1.0", | ||||
|     "@tsconfig/node20": "^20.1.4", | ||||
|     "@types/dompurify": "^3.0.5", | ||||
| @@ -23,12 +20,14 @@ | ||||
|     "bindings": "^1.5.0", | ||||
|     "bootstrap": "5.2.3", | ||||
|     "chokidar": "^3.5.1", | ||||
|     "ckeditor5": "^44.1.0", | ||||
|     "dompurify": "^3.1.0", | ||||
|     "eslint": "^9.14.0", | ||||
|     "eslint-config-prettier": "^9.1.0", | ||||
|     "eslint-plugin-prettier": "^5.2.1", | ||||
|     "eslint-plugin-vue": "^9.30.0", | ||||
|     "fork-awesome": "^1.1.7", | ||||
|     "intl-messageformat": "^10.5.11", | ||||
|     "jquery": "^3.6.0", | ||||
|     "node-sass": "^8.0.0", | ||||
|     "popper.js": "^1.16.1", | ||||
| @@ -54,11 +53,13 @@ | ||||
|     "@fullcalendar/timegrid": "^6.1.4", | ||||
|     "@fullcalendar/vue3": "^6.1.4", | ||||
|     "@popperjs/core": "^2.9.2", | ||||
|     "@tsconfig/node20": "^20.1.4", | ||||
|     "@types/dompurify": "^3.0.5", | ||||
|     "@types/leaflet": "^1.9.3", | ||||
|     "bootstrap-icons": "^1.11.3", | ||||
|     "dropzone": "^5.7.6", | ||||
|     "es6-promise": "^4.2.8", | ||||
|     "intl-messageformat": "^10.5.11", | ||||
|     "leaflet": "^1.7.1", | ||||
|     "marked": "^12.0.2", | ||||
|     "masonry-layout": "^4.2.2", | ||||
| @@ -72,7 +73,7 @@ | ||||
|     "vuex": "^4.0.0" | ||||
|   }, | ||||
|   "browserslist": [ | ||||
|     "Firefox ESR" | ||||
|     "defaults and fully supports es6-module and not dead" | ||||
|   ], | ||||
|   "scripts": { | ||||
|     "dev-server": "encore dev-server", | ||||
|   | ||||
| @@ -11,6 +11,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\ActivityBundle\Menu; | ||||
|  | ||||
| use Chill\ActivityBundle\Entity\Activity; | ||||
| use Chill\ActivityBundle\Security\Authorization\ActivityVoter; | ||||
| use Chill\MainBundle\Routing\LocalMenuBuilderInterface; | ||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||
| @@ -23,22 +24,30 @@ use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  */ | ||||
| class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface | ||||
| { | ||||
|     public function __construct(protected Security $security, protected TranslatorInterface $translator) {} | ||||
|     public function __construct( | ||||
|         protected Security $security, | ||||
|         protected TranslatorInterface $translator, | ||||
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildMenu($menuId, MenuItem $menu, array $parameters) | ||||
|     { | ||||
|         $period = $parameters['accompanyingCourse']; | ||||
|  | ||||
|         $activities = $this->managerRegistry->getManager()->getRepository(Activity::class)->findBy( | ||||
|             ['accompanyingPeriod' => $period] | ||||
|         ); | ||||
|  | ||||
|         if ( | ||||
|             AccompanyingPeriod::STEP_DRAFT !== $period->getStep() | ||||
|             && $this->security->isGranted(ActivityVoter::SEE, $period) | ||||
|         ) { | ||||
|             $menu->addChild($this->translator->trans('Activity'), [ | ||||
|             $menu->addChild($this->translator->trans('Activities'), [ | ||||
|                 'route' => 'chill_activity_activity_list', | ||||
|                 'routeParameters' => [ | ||||
|                     'accompanying_period_id' => $period->getId(), | ||||
|                 ], ]) | ||||
|                 ->setExtras(['order' => 40]); | ||||
|                 ->setExtras(['order' => 40, 'counter' => count($activities) > 0 ? count($activities) : null]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Chill\ActivityBundle\Menu; | ||||
|  | ||||
| use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; | ||||
| use Chill\ActivityBundle\Security\Authorization\ActivityVoter; | ||||
| use Chill\MainBundle\Routing\LocalMenuBuilderInterface; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| @@ -23,13 +24,20 @@ use Symfony\Contracts\Translation\TranslatorInterface; | ||||
|  */ | ||||
| final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface | ||||
| { | ||||
|     public function __construct(private AuthorizationCheckerInterface $authorizationChecker, private TranslatorInterface $translator) {} | ||||
|     public function __construct( | ||||
|         private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository, | ||||
|         private AuthorizationCheckerInterface $authorizationChecker, | ||||
|         private TranslatorInterface $translator, | ||||
|     ) {} | ||||
|  | ||||
|     public function buildMenu($menuId, MenuItem $menu, array $parameters) | ||||
|     { | ||||
|         /** @var Person $person */ | ||||
|         $person = $parameters['person']; | ||||
|  | ||||
|  | ||||
|         $count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE); | ||||
|  | ||||
|         if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) { | ||||
|             $menu->addChild( | ||||
|                 $this->translator->trans('Activities'), | ||||
| @@ -38,7 +46,7 @@ final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface | ||||
|                     'routeParameters' => ['person_id' => $person->getId()], | ||||
|                 ] | ||||
|             ) | ||||
|                 ->setExtra('order', 201); | ||||
|                 ->setExtras(['order' => 201, 'counter' => $count > 0 ? $count : null]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -120,3 +120,34 @@ li.document-list-item { | ||||
|         vertical-align: baseline; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .badge-activity-type-simple { | ||||
|     @extend .badge; | ||||
|     display: inline-block; | ||||
|     margin: 0.2rem 0; | ||||
|     padding-left: 0; | ||||
|     padding-right: 0.5rem; | ||||
|  | ||||
|     border-left: 20px groove #9acd32; | ||||
|     border-radius: $badge-border-radius; | ||||
|  | ||||
|     color: black; | ||||
|     font-weight: normal; | ||||
|     font-size: unset; | ||||
|     max-width: 100%; | ||||
|     background-color: $gray-100; | ||||
|  | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
|     text-indent: 5px hanging; | ||||
|     text-align: left; | ||||
|  | ||||
|     &::before { | ||||
|         margin-right: 3px; | ||||
|         position: relative; | ||||
|         left: -0.5px; | ||||
|         font-family: ForkAwesome; | ||||
|         content: '\f04b'; | ||||
|         color: #9acd32; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import Location from "./components/Location.vue"; | ||||
|  | ||||
| export default { | ||||
|     name: "App", | ||||
|     props: ["hasSocialIssues", "hasLocation", "hasPerson"], | ||||
|     props: ["hasSocialIssues", "hasLocation", "hasPerson", "isSimpleEditor"], | ||||
|     components: { | ||||
|         ConcernedGroups, | ||||
|         SocialIssuesAcc, | ||||
|   | ||||
| @@ -30,13 +30,14 @@ | ||||
|         <ul class="record_actions"> | ||||
|             <li class="add-persons"> | ||||
|                 <add-persons | ||||
|                     button-title="activity.add_persons" | ||||
|                     modal-title="activity.add_persons" | ||||
|                     :key="addPersons.key" | ||||
|                     :options="addPersonsOptions" | ||||
|                     @add-new-persons="addNewPersons" | ||||
|                     :buttonTitle="trans(ACTIVITY_ADD_PERSONS)" | ||||
|                     :modalTitle="trans(ACTIVITY_ADD_PERSONS)" | ||||
|                     v-bind:key="addPersons.key" | ||||
|                     v-bind:options="addPersonsOptions" | ||||
|                     @addNewPersons="addNewPersons" | ||||
|                     ref="addPersons" | ||||
|                 /> | ||||
|                 > | ||||
|                 </add-persons> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </teleport> | ||||
| @@ -47,6 +48,14 @@ import { mapState, mapGetters } from "vuex"; | ||||
| import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue"; | ||||
| import PersonsBloc from "./ConcernedGroups/PersonsBloc.vue"; | ||||
| import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue"; | ||||
| import { | ||||
|     ACTIVITY_BLOC_PERSONS, | ||||
|     ACTIVITY_BLOC_PERSONS_ASSOCIATED, | ||||
|     ACTIVITY_BLOC_THIRDPARTY, | ||||
|     ACTIVITY_BLOC_USERS, | ||||
|     ACTIVITY_ADD_PERSONS, | ||||
|     trans, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "ConcernedGroups", | ||||
| @@ -55,18 +64,24 @@ export default { | ||||
|         PersonsBloc, | ||||
|         PersonText, | ||||
|     }, | ||||
|     setup() { | ||||
|         return { | ||||
|             trans, | ||||
|             ACTIVITY_ADD_PERSONS, | ||||
|         }; | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             personsBlocs: [ | ||||
|                 { | ||||
|                     key: "persons", | ||||
|                     title: "activity.bloc_persons", | ||||
|                     title: trans(ACTIVITY_BLOC_PERSONS), | ||||
|                     persons: [], | ||||
|                     included: false, | ||||
|                 }, | ||||
|                 { | ||||
|                     key: "personsAssociated", | ||||
|                     title: "activity.bloc_persons_associated", | ||||
|                     title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED), | ||||
|                     persons: [], | ||||
|                     included: window.activity | ||||
|                         ? window.activity.activityType.personsVisible !== 0 | ||||
| @@ -82,7 +97,7 @@ export default { | ||||
|                 }, | ||||
|                 { | ||||
|                     key: "thirdparty", | ||||
|                     title: "activity.bloc_thirdparty", | ||||
|                     title: trans(ACTIVITY_BLOC_THIRDPARTY), | ||||
|                     persons: [], | ||||
|                     included: window.activity | ||||
|                         ? window.activity.activityType.thirdPartiesVisible !== 0 | ||||
| @@ -90,7 +105,7 @@ export default { | ||||
|                 }, | ||||
|                 { | ||||
|                     key: "users", | ||||
|                     title: "activity.bloc_users", | ||||
|                     title: trans(ACTIVITY_BLOC_USERS), | ||||
|                     persons: [], | ||||
|                     included: window.activity | ||||
|                         ? window.activity.activityType.usersVisible !== 0 | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     <teleport to="#location"> | ||||
|         <div class="mb-3 row"> | ||||
|             <label :class="locationClassList"> | ||||
|                 {{ $t("activity.location") }} | ||||
|                 {{ trans(ACTIVITY_LOCATION) }} | ||||
|             </label> | ||||
|             <div class="col-sm-8"> | ||||
|                 <VueMultiselect | ||||
| @@ -13,17 +13,17 @@ | ||||
|                     open-direction="top" | ||||
|                     :multiple="false" | ||||
|                     :searchable="true" | ||||
|                     :placeholder="$t('activity.choose_location')" | ||||
|                     :placeholder="trans(ACTIVITY_CHOOSE_LOCATION)" | ||||
|                     :custom-label="customLabel" | ||||
|                     :select-label="$t('multiselect.select_label')" | ||||
|                     :deselect-label="$t('multiselect.deselect_label')" | ||||
|                     :selected-label="$t('multiselect.selected_label')" | ||||
|                     :select-label="trans(MULTISELECT_SELECT_LABEL)" | ||||
|                     :deselect-label="trans(MULTISELECT_DESELECT_LABEL)" | ||||
|                     :selected-label="trans(MULTISELECT_SELECTED_LABEL)" | ||||
|                     :options="availableLocations" | ||||
|                     group-values="locations" | ||||
|                     group-label="locationGroup" | ||||
|                     v-model="location" | ||||
|                 /> | ||||
|                 <new-location :available-locations="availableLocations" /> | ||||
|                 <new-location v-bind:available-locations="availableLocations" /> | ||||
|             </div> | ||||
|         </div> | ||||
|     </teleport> | ||||
| @@ -33,6 +33,14 @@ | ||||
| import { mapState, mapGetters } from "vuex"; | ||||
| import VueMultiselect from "vue-multiselect"; | ||||
| import NewLocation from "./Location/NewLocation.vue"; | ||||
| import { | ||||
|     trans, | ||||
|     ACTIVITY_LOCATION, | ||||
|     ACTIVITY_CHOOSE_LOCATION, | ||||
|     MULTISELECT_SELECT_LABEL, | ||||
|     MULTISELECT_DESELECT_LABEL, | ||||
|     MULTISELECT_SELECTED_LABEL, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "Location", | ||||
| @@ -40,6 +48,16 @@ export default { | ||||
|         NewLocation, | ||||
|         VueMultiselect, | ||||
|     }, | ||||
|     setup() { | ||||
|         return { | ||||
|             trans, | ||||
|             ACTIVITY_LOCATION, | ||||
|             ACTIVITY_CHOOSE_LOCATION, | ||||
|             MULTISELECT_SELECT_LABEL, | ||||
|             MULTISELECT_DESELECT_LABEL, | ||||
|             MULTISELECT_SELECTED_LABEL, | ||||
|         }; | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`, | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|         <ul class="record_actions"> | ||||
|             <li> | ||||
|                 <a class="btn btn-sm btn-create" @click="openModal"> | ||||
|                     {{ $t("activity.create_new_location") }} | ||||
|                     {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }} | ||||
|                 </a> | ||||
|             </li> | ||||
|         </ul> | ||||
| @@ -11,12 +11,12 @@ | ||||
|         <teleport to="body"> | ||||
|             <modal | ||||
|                 v-if="modal.showModal" | ||||
|                 :modal-dialog-class="modal.modalDialogClass" | ||||
|                 :modalDialogClass="modal.modalDialogClass" | ||||
|                 @close="modal.showModal = false" | ||||
|             > | ||||
|                 <template #header> | ||||
|                     <h3 class="modal-title"> | ||||
|                         {{ $t("activity.create_new_location") }} | ||||
|                         {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }} | ||||
|                     </h3> | ||||
|                 </template> | ||||
|                 <template #body> | ||||
| @@ -37,7 +37,7 @@ | ||||
|                                 v-model="selectType" | ||||
|                             > | ||||
|                                 <option selected disabled value=""> | ||||
|                                     {{ $t("activity.choose_location_type") }} | ||||
|                                     {{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }} | ||||
|                                 </option> | ||||
|                                 <option | ||||
|                                     v-for="t in locationTypes" | ||||
| @@ -48,7 +48,7 @@ | ||||
|                                 </option> | ||||
|                             </select> | ||||
|                             <label>{{ | ||||
|                                 $t("activity.location_fields.type") | ||||
|                                 trans(ACTIVITY_LOCATION_FIELDS_TYPE) | ||||
|                             }}</label> | ||||
|                         </div> | ||||
|  | ||||
| @@ -60,14 +60,14 @@ | ||||
|                                 placeholder | ||||
|                             /> | ||||
|                             <label for="name">{{ | ||||
|                                 $t("activity.location_fields.name") | ||||
|                                 trans(ACTIVITY_LOCATION_FIELDS_NAME) | ||||
|                             }}</label> | ||||
|                         </div> | ||||
|  | ||||
|                         <add-address | ||||
|                             :context="addAddress.context" | ||||
|                             :options="addAddress.options" | ||||
|                             :address-changed-callback="submitNewAddress" | ||||
|                             :addressChangedCallback="submitNewAddress" | ||||
|                             v-if="showAddAddress" | ||||
|                             ref="addAddress" | ||||
|                         /> | ||||
| @@ -80,7 +80,7 @@ | ||||
|                                 placeholder | ||||
|                             /> | ||||
|                             <label for="phonenumber1">{{ | ||||
|                                 $t("activity.location_fields.phonenumber1") | ||||
|                                 trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1) | ||||
|                             }}</label> | ||||
|                         </div> | ||||
|                         <div class="form-floating mb-3" v-if="hasPhonenumber1"> | ||||
| @@ -91,7 +91,7 @@ | ||||
|                                 placeholder | ||||
|                             /> | ||||
|                             <label for="phonenumber2">{{ | ||||
|                                 $t("activity.location_fields.phonenumber2") | ||||
|                                 trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2) | ||||
|                             }}</label> | ||||
|                         </div> | ||||
|                         <div class="form-floating mb-3" v-if="showContactData"> | ||||
| @@ -102,7 +102,7 @@ | ||||
|                                 placeholder | ||||
|                             /> | ||||
|                             <label for="email">{{ | ||||
|                                 $t("activity.location_fields.email") | ||||
|                                 trans(ACTIVITY_LOCATION_FIELDS_EMAIL) | ||||
|                             }}</label> | ||||
|                         </div> | ||||
|                     </form> | ||||
| @@ -112,7 +112,7 @@ | ||||
|                         class="btn btn-save" | ||||
|                         @click.prevent="saveNewLocation" | ||||
|                     > | ||||
|                         {{ $t("action.save") }} | ||||
|                         {{ trans(SAVE) }} | ||||
|                     </button> | ||||
|                 </template> | ||||
|             </modal> | ||||
| @@ -126,6 +126,17 @@ import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue" | ||||
| import { mapState } from "vuex"; | ||||
| import { getLocationTypes } from "../../api"; | ||||
| import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; | ||||
| 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, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "NewLocation", | ||||
| @@ -133,6 +144,19 @@ export default { | ||||
|         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 { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|         <div class="mb-3 row"> | ||||
|             <div class="col-4"> | ||||
|                 <label :class="socialIssuesClassList">{{ | ||||
|                     $t("activity.social_issues") | ||||
|                     trans(ACTIVITY_SOCIAL_ISSUES) | ||||
|                 }}</label> | ||||
|             </div> | ||||
|             <div class="col-8"> | ||||
| @@ -12,8 +12,9 @@ | ||||
|                     :key="issue.id" | ||||
|                     :issue="issue" | ||||
|                     :selection="socialIssuesSelected" | ||||
|                     @update-selected="updateIssuesSelected" | ||||
|                 /> | ||||
|                     @updateSelected="updateIssuesSelected" | ||||
|                 > | ||||
|                 </check-social-issue> | ||||
|  | ||||
|                 <div class="my-3"> | ||||
|                     <VueMultiselect | ||||
| @@ -31,10 +32,11 @@ | ||||
|                         :allow-empty="true" | ||||
|                         :show-labels="false" | ||||
|                         :loading="issueIsLoading" | ||||
|                         :placeholder="$t('activity.choose_other_social_issue')" | ||||
|                         :placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)" | ||||
|                         :options="socialIssuesOther" | ||||
|                         @select="addIssueInList" | ||||
|                     /> | ||||
|                     > | ||||
|                     </VueMultiselect> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
| @@ -42,35 +44,46 @@ | ||||
|         <div class="mb-3 row"> | ||||
|             <div class="col-4"> | ||||
|                 <label :class="socialActionsClassList">{{ | ||||
|                     $t("activity.social_actions") | ||||
|                     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 | ||||
|                         class="chill-green fa fa-circle-o-notch fa-spin fa-lg" | ||||
|                     ></i> | ||||
|                 </div> | ||||
|  | ||||
|                 <span | ||||
|                     v-else-if="socialIssuesSelected.length === 0" | ||||
|                     class="inline-choice chill-no-data-statement mt-3" | ||||
|                 > | ||||
|                     {{ $t("activity.select_first_a_social_issue") }} | ||||
|                     {{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }} | ||||
|                 </span> | ||||
|  | ||||
|                 <template v-else-if="socialActionsList.length > 0"> | ||||
|                 <template | ||||
|                     v-else-if=" | ||||
|                         socialActionsList.length > 0 && | ||||
|                         (socialIssuesSelected.length || | ||||
|                             socialActionsSelected.length) | ||||
|                     " | ||||
|                 > | ||||
|                     <div | ||||
|                         v-if=" | ||||
|                             socialIssuesSelected.length || | ||||
|                             socialActionsSelected.length | ||||
|                         " | ||||
|                         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 socialActionsList" | ||||
|                             v-for="action in group.actions" | ||||
|                             :key="action.id" | ||||
|                             :action="action" | ||||
|                             :selection="socialActionsSelected" | ||||
|                             @update-selected="updateActionsSelected" | ||||
|                         /> | ||||
|                             @updateSelected="updateActionsSelected" | ||||
|                         > | ||||
|                         </check-social-action> | ||||
|                     </div> | ||||
|                 </template> | ||||
|  | ||||
| @@ -80,7 +93,7 @@ | ||||
|                     " | ||||
|                     class="inline-choice chill-no-data-statement mt-3" | ||||
|                 > | ||||
|                     {{ $t("activity.social_action_list_empty") }} | ||||
|                     {{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }} | ||||
|                 </span> | ||||
|             </div> | ||||
|         </div> | ||||
| @@ -92,6 +105,14 @@ import VueMultiselect from "vue-multiselect"; | ||||
| 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, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "SocialIssuesAcc", | ||||
| @@ -100,6 +121,16 @@ export default { | ||||
|         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, | ||||
| @@ -127,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, reject) => { | ||||
|                     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), | ||||
| @@ -208,7 +230,7 @@ export default { | ||||
|                 this.actionIsLoading = true; | ||||
|                 getSocialActionByIssue(item.id).then( | ||||
|                     (actions) => | ||||
|                         new Promise((resolve, reject) => { | ||||
|                         new Promise((resolve) => { | ||||
|                             actions.results.forEach((action) => { | ||||
|                                 this.$store.commit("addActionInList", action); | ||||
|                             }, this); | ||||
| @@ -235,9 +257,24 @@ export default { | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style src="vue-multiselect/dist/vue-multiselect.css"></style> | ||||
| <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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -14,18 +14,21 @@ const i18n = _createI18n(activityMessages); | ||||
| const hasSocialIssues = document.querySelector("#social-issues-acc") !== null; | ||||
| const hasLocation = document.querySelector("#location") !== null; | ||||
| const hasPerson = document.querySelector("#add-persons") !== null; | ||||
| const isSimpleEditor = true; | ||||
|  | ||||
| const app = createApp({ | ||||
|   template: `<app | ||||
|        :hasSocialIssues="hasSocialIssues" | ||||
|        :hasLocation="hasLocation" | ||||
|        :hasPerson="hasPerson" | ||||
|        :isSimpleEditor = "isSimpleEditor" | ||||
|     ></app>`, | ||||
|   data() { | ||||
|     return { | ||||
|       hasSocialIssues, | ||||
|       hasLocation, | ||||
|       hasPerson, | ||||
|       isSimpleEditor | ||||
|     }; | ||||
|   }, | ||||
| }) | ||||
|   | ||||
| @@ -26,6 +26,7 @@ const store = createStore({ | ||||
|   state: { | ||||
|     me: null, | ||||
|     activity: window.activity, | ||||
|     accompanyingPeriodWorks: [], | ||||
|     socialIssuesOther: [], | ||||
|     socialActionsList: [], | ||||
|     availableLocations: [], | ||||
| @@ -41,7 +42,7 @@ const store = createStore({ | ||||
|       const allEntities = [ | ||||
|         ...store.getters.suggestedPersons, | ||||
|         ...store.getters.suggestedRequestor, | ||||
|         ...store.getters.suggestedUser, | ||||
|         ...store.getters.suggestedUsers, | ||||
|         ...store.getters.suggestedResources, | ||||
|       ]; | ||||
|       const uniqueIds = [ | ||||
| @@ -80,8 +81,7 @@ const store = createStore({ | ||||
|             state.activity.activityType.thirdPartiesVisible !== 0), | ||||
|       ); | ||||
|     }, | ||||
|     suggestedUser(state) { | ||||
|       // console.log('current user', state.me) | ||||
|     suggestedUsers(state) { | ||||
|       const existingUserIds = state.activity.users.map((p) => p.id); | ||||
|       let suggestedUsers = | ||||
|         state.activity.activityType.usersVisible === 0 | ||||
| @@ -90,11 +90,18 @@ const store = createStore({ | ||||
|               (u) => u !== null && !existingUserIds.includes(u.id), | ||||
|             ); | ||||
|  | ||||
|       state.accompanyingPeriodWorks.forEach((work) => { | ||||
|         work.referrers.forEach((r) => { | ||||
|           if (!existingUserIds.includes(r.id)) { | ||||
|             suggestedUsers.push(r); | ||||
|           } | ||||
|         }); | ||||
|       }); | ||||
|       // Add the current user from the state | ||||
|       if (state.me && !existingUserIds.includes(state.me.id)) { | ||||
|         suggestedUsers.push(state.me); | ||||
|       } | ||||
|       console.log("suggested users", suggestedUsers); | ||||
|       // console.log("suggested users", suggestedUsers); | ||||
|  | ||||
|       return suggestedUsers; | ||||
|     }, | ||||
| @@ -117,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: { | ||||
| @@ -223,6 +240,9 @@ const store = createStore({ | ||||
|     addAvailableLocationGroup(state, group) { | ||||
|       state.availableLocations.push(group); | ||||
|     }, | ||||
|     setAccompanyingPeriodWorks(state, works) { | ||||
|       state.accompanyingPeriodWorks = works; | ||||
|     }, | ||||
|   }, | ||||
|   actions: { | ||||
|     addIssueSelected({ commit }, issue) { | ||||
| @@ -341,6 +361,17 @@ const store = createStore({ | ||||
|       } | ||||
|       commit("updateLocation", value); | ||||
|     }, | ||||
|     async fetchAccompanyingPeriodWorks({ state, commit }) { | ||||
|       const accompanyingPeriodId = state.activity.accompanyingPeriod.id; | ||||
|       const url = `/api/1.0/person/accompanying-course/${accompanyingPeriodId}/works.json`; | ||||
|       try { | ||||
|         const works = await makeFetch("GET", url); | ||||
|         // console.log("works", works); | ||||
|         commit("setAccompanyingPeriodWorks", works); | ||||
|       } catch (error) { | ||||
|         console.error("Failed to fetch accompanying period works:", error); | ||||
|       } | ||||
|     }, | ||||
|     getWhoAmI({ commit }) { | ||||
|       const url = `/api/1.0/main/whoami.json`; | ||||
|       makeFetch("GET", url).then((user) => { | ||||
| @@ -353,5 +384,6 @@ const store = createStore({ | ||||
| store.dispatch("getWhoAmI"); | ||||
|  | ||||
| prepareLocations(store); | ||||
| store.dispatch("fetchAccompanyingPeriodWorks"); | ||||
|  | ||||
| export default store; | ||||
|   | ||||
| @@ -126,4 +126,4 @@ | ||||
|  | ||||
| {% block css %} | ||||
|     {{ encore_entry_link_tags('mod_pickentity_type') }} | ||||
| {% endblock %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -13,44 +13,44 @@ | ||||
| {% endif %} | ||||
|  | ||||
| <div class="item-row"> | ||||
|     <div class="item-col" style="width: unset"> | ||||
|         {% if document.isPending %} | ||||
|             <div class="badge text-bg-info" data-docgen-is-pending="{{ document.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|         {% elseif document.isFailure %} | ||||
|             <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|         {% endif %} | ||||
|  | ||||
|         <div> | ||||
|             {% if activity.accompanyingPeriod is not null and context == 'person' %} | ||||
|                 <span class="badge bg-primary"> | ||||
|                         <i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }} | ||||
|                     </span>  | ||||
|     <div class="item-two-col-grid"> | ||||
|         <div class="title"> | ||||
|             {% if document.isPending %} | ||||
|                 <div class="badge text-bg-info" data-docgen-is-pending="{{ document.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|             {% elseif document.isFailure %} | ||||
|                 <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|             {% endif %} | ||||
|             <div class="badge-activity-type"> | ||||
|                 <span class="title_label"></span> | ||||
|                 <span class="title_action"> | ||||
|                     {{ activity.type.name | localize_translatable_string }} | ||||
|  | ||||
|             <div> | ||||
|                 <div> | ||||
|                     <div class="badge-activity-type-simple"> | ||||
|                         {{ activity.type.name | localize_translatable_string }} | ||||
|                     </div> | ||||
|                     {% if activity.emergency %} | ||||
|                         <span class="badge bg-danger rounded-pill fs-6 float-end">{{ 'Emergency'|trans|upper }}</span> | ||||
|                     {% endif %} | ||||
|                     </span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="denomination h2"> | ||||
|             {{ document.title|chill_print_or_message("No title") }} | ||||
|         </div> | ||||
|         {% if document.hasTemplate %} | ||||
|             <div> | ||||
|                 <p>{{ document.template.name|localize_translatable_string }}</p> | ||||
|             <div class="denomination h2"> | ||||
|                 {{ document.title|chill_print_or_message("No title") }} | ||||
|             </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
|     <div class="item-col"> | ||||
|         <div class="container"> | ||||
|             {% if document.hasTemplate %} | ||||
|                 <div> | ||||
|                     <p>{{ document.template.name|localize_translatable_string }}</p> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|         <div class="aside"> | ||||
|             <div class="dates row text-end"> | ||||
|                 <span>{{ document.createdAt|format_date('short') }}</span> | ||||
|             </div> | ||||
|             {% if activity.accompanyingPeriod is not null and context == 'person' %} | ||||
|                 <div class="text-end"> | ||||
|                     <span class="badge bg-primary"> | ||||
|                         <i class="fa fa-random"></i> {{ activity.accompanyingPeriod.id }} | ||||
|                     </span>  | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -143,7 +143,10 @@ class ListActivitiesByAccompanyingPeriodContext implements | ||||
|             array_filter( | ||||
|                 $works, | ||||
|                 function ($work) use ($user) { | ||||
|                     $workUsernames = array_map(static fn (User $user) => $user['username'], $work['referrers'] ?? []); | ||||
|                     $workUsernames = []; | ||||
|                     foreach ($work['referrers'] as $referrer) { | ||||
|                         $workUsernames[] = $referrer['username']; | ||||
|                     } | ||||
|  | ||||
|                     return \in_array($user->getUserIdentifier(), $workUsernames, true); | ||||
|                 } | ||||
|   | ||||
| @@ -102,6 +102,32 @@ activity: | ||||
|     Remove a document: Supprimer le document | ||||
|     comment: Commentaire | ||||
|     deleted: Échange supprimé | ||||
|  | ||||
|     errors: Le formulaire contient des erreurs | ||||
|     social_issues: Problématiques sociales | ||||
|     choose_other_social_issue: Ajouter une autre problématique sociale... | ||||
|     social_actions: Actions d'accompagnement | ||||
|     select_first_a_social_issue: Sélectionnez d'abord une problématique sociale | ||||
|     social_action_list_empty: Aucune action sociale disponible | ||||
|     add_persons: Ajouter des personnes concernées | ||||
|     bloc_persons: Usagers | ||||
|     bloc_persons_associated: Usagers du parcours | ||||
|     bloc_persons_not_associated: Tiers non-pro. | ||||
|     bloc_thirdparty: Tiers professionnels | ||||
|     bloc_users: T(M)S | ||||
|     location: Localisation | ||||
|     choose_location: Choisissez une localisation | ||||
|     choose_location_type: Choisissez un type de localisation | ||||
|     create_new_location: Créer une nouvelle localisation | ||||
|     location_fields: | ||||
|         name: Nom | ||||
|         type: Type | ||||
|         phonenumber1: Téléphone | ||||
|         phonenumber2: Autre téléphone | ||||
|         email: Adresse courriel | ||||
|     create_address: Créer une adresse | ||||
|     edit_address: Modifier l'adresse | ||||
|  | ||||
| No documents: Aucun document | ||||
|  | ||||
| # activity filter in list page | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| @import '~ChillPersonAssets/chill/scss/mixins.scss'; | ||||
| @import '~ChillMainAssets/module/bootstrap/shared'; | ||||
| @import '~ChillPersonAssets/chill/scss/mixins.scss'; | ||||
| @import 'bootstrap/scss/_badge.scss'; | ||||
|  | ||||
| .badge-calendar { | ||||
|     display: inline-block; | ||||
| @@ -23,3 +24,35 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| .badge-calendar-simple { | ||||
|     @extend .badge; | ||||
|     display: inline-block; | ||||
|     margin: 0.2rem 0; | ||||
|     padding-left: 0; | ||||
|     padding-right: 0.5rem; | ||||
|  | ||||
|     border-left: 20px groove $chill-l-gray; | ||||
|     border-radius: $badge-border-radius; | ||||
|  | ||||
|     max-width: 100%; | ||||
|     background-color: $gray-100; | ||||
|  | ||||
|     color: black; | ||||
|     font-weight: normal; | ||||
|     overflow: hidden; | ||||
|     font-weight: normal; | ||||
|     font-size: unset; | ||||
|     text-overflow: ellipsis; | ||||
|     text-indent: 5px hanging; | ||||
|     text-align: left; | ||||
|  | ||||
|     &::before { | ||||
|         margin-right: 3px; | ||||
|         position: relative; | ||||
|         left: -0.5px; | ||||
|         font-family: ForkAwesome; | ||||
|         content: '\f04b'; | ||||
|         color: $chill-l-gray; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,7 +16,7 @@ div.calendar-list { | ||||
|     } | ||||
|  | ||||
|     & > a.calendar-list__global { | ||||
|         display: inline-block;; | ||||
|         display: inline-block; | ||||
|         padding: 0.2rem; | ||||
|         min-width: 2rem; | ||||
|         border: 1px solid var(--bs-chill-blue); | ||||
|   | ||||
| @@ -96,23 +96,23 @@ | ||||
|         </div> | ||||
|     </div> | ||||
|     <FullCalendar :options="calendarOptions" ref="calendarRef"> | ||||
|         <template v-slot:eventContent="arg: EventApi"> | ||||
|             <span :class="eventClasses(arg.event)"> | ||||
|                 <b v-if="arg.event.extendedProps.is === 'remote'">{{ | ||||
|                     arg.event.title | ||||
|         <template v-slot:eventContent="{ event }"> | ||||
|             <span :class="eventClasses(event)"> | ||||
|                 <b v-if="event.extendedProps.is === 'remote'">{{ | ||||
|                     event.title | ||||
|                 }}</b> | ||||
|                 <b v-else-if="arg.event.extendedProps.is === 'range'" | ||||
|                     >{{ arg.timeText }} - | ||||
|                     {{ arg.event.extendedProps.locationName }}</b | ||||
|                 <b v-else-if="event.extendedProps.is === 'range'" | ||||
|                     >{{ formatDate(event.startStr) }} - | ||||
|                     {{ event.extendedProps.locationName }}</b | ||||
|                 > | ||||
|                 <b v-else-if="arg.event.extendedProps.is === 'local'">{{ | ||||
|                     arg.event.title | ||||
|                 <b v-else-if="event.extendedProps.is === 'local'">{{ | ||||
|                     event.title | ||||
|                 }}</b> | ||||
|                 <b v-else>no 'is'</b> | ||||
|                 <a | ||||
|                     v-if="arg.event.extendedProps.is === 'range'" | ||||
|                     v-if="event.extendedProps.is === 'range'" | ||||
|                     class="fa fa-fw fa-times delete" | ||||
|                     @click.prevent="onClickDelete(arg.event)" | ||||
|                     @click.prevent="onClickDelete(event)" | ||||
|                 > | ||||
|                 </a> | ||||
|             </span> | ||||
| @@ -221,13 +221,12 @@ import type { | ||||
|     DatesSetArg, | ||||
|     EventInput, | ||||
| } from "@fullcalendar/core"; | ||||
| import { reactive, computed, ref, onMounted } from "vue"; | ||||
| import { computed, ref, onMounted } from "vue"; | ||||
| import { useStore } from "vuex"; | ||||
| import { key } from "./store"; | ||||
| import FullCalendar from "@fullcalendar/vue3"; | ||||
| import frLocale from "@fullcalendar/core/locales/fr"; | ||||
| import interactionPlugin, { | ||||
|     DropArg, | ||||
|     EventResizeDoneArg, | ||||
| } from "@fullcalendar/interaction"; | ||||
| import timeGridPlugin from "@fullcalendar/timegrid"; | ||||
| @@ -237,19 +236,13 @@ import { | ||||
|     EventDropArg, | ||||
|     EventClickArg, | ||||
| } from "@fullcalendar/core"; | ||||
| import { | ||||
|     dateToISO, | ||||
|     ISOToDate, | ||||
| } from "../../../../../ChillMainBundle/Resources/public/chill/js/date"; | ||||
| import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date"; | ||||
| import VueMultiselect from "vue-multiselect"; | ||||
| import { Location } from "../../../../../ChillMainBundle/Resources/public/types"; | ||||
| import { Location } from "ChillMainAssets/types"; | ||||
| import EditLocation from "./Components/EditLocation.vue"; | ||||
| import { useI18n } from "vue-i18n"; | ||||
|  | ||||
| const store = useStore(key); | ||||
|  | ||||
| const { t } = useI18n(); | ||||
|  | ||||
| const showWeekends = ref(false); | ||||
| const slotDuration = ref("00:15:00"); | ||||
| const slotMinTime = ref("09:00:00"); | ||||
| @@ -301,6 +294,11 @@ const nextWeeks = computed((): Weeks[] => | ||||
|     }), | ||||
| ); | ||||
|  | ||||
| const formatDate = (datetime: string) => { | ||||
|     console.log(typeof datetime); | ||||
|     return ISOToDate(datetime); | ||||
| }; | ||||
|  | ||||
| const baseOptions = ref<CalendarOptions>({ | ||||
|     locale: frLocale, | ||||
|     plugins: [interactionPlugin, timeGridPlugin], | ||||
| @@ -353,7 +351,7 @@ const pickedLocation = computed<Location | null>({ | ||||
|  * return the show classes for the event | ||||
|  * @param arg | ||||
|  */ | ||||
| const eventClasses = function (arg: EventApi): object { | ||||
| const eventClasses = function (): object { | ||||
|     return { calendarRangeItems: true }; | ||||
| }; | ||||
|  | ||||
| @@ -431,7 +429,6 @@ function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) { | ||||
|     if (payload.event.extendedProps.is !== "range") { | ||||
|         return; | ||||
|     } | ||||
|     const changedEvent = payload.event; | ||||
|  | ||||
|     store.dispatch("calendarRanges/patchRangeTime", { | ||||
|         calendarRangeId: payload.event.extendedProps.calendarRangeId, | ||||
|   | ||||
| @@ -6,50 +6,48 @@ | ||||
|  | ||||
|  | ||||
| <div class="item-row"> | ||||
|     <div class="item-col" style="width: unset"> | ||||
|         {% if document.storedObject.isPending %} | ||||
|             <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|         {% elseif document.storedObject.isFailure %} | ||||
|             <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|         {% endif %} | ||||
|  | ||||
|         <div> | ||||
|             {% if c.accompanyingPeriod is not null and context == 'person' %} | ||||
|                 <span class="badge bg-primary"> | ||||
|                         <i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }} | ||||
|                     </span>  | ||||
|     <div class="item-two-col-grid"> | ||||
|         <div class="title"> | ||||
|             {% if document.storedObject.isPending %} | ||||
|                 <div class="badge text-bg-info" data-docgen-is-pending="{{ document.storedObject.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|             {% elseif document.storedObject.isFailure %} | ||||
|                 <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|             {% endif %} | ||||
|  | ||||
|             <span class="badge-calendar"> | ||||
|                     <span class="title_label"></span> | ||||
|                     <span class="title_action"> | ||||
|                         {{ 'Calendar'|trans }} | ||||
|                         {% if c.endDate.diff(c.startDate).days >= 1 %} | ||||
|                             {{ c.startDate|format_datetime('short', 'short') }} | ||||
|                             - {{ c.endDate|format_datetime('short', 'short') }} | ||||
|                         {% else %} | ||||
|                             {{ c.startDate|format_datetime('short', 'short') }} | ||||
|                             - {{ c.endDate|format_datetime('none', 'short') }} | ||||
|                         {% endif %} | ||||
|                     </span> | ||||
|                 </span> | ||||
|         </div> | ||||
|  | ||||
|         <div class="denomination h2"> | ||||
|             {{ document.storedObject.title|chill_print_or_message("No title") }} | ||||
|         </div> | ||||
|         {% if document.storedObject.hasTemplate %} | ||||
|             <div> | ||||
|                 <p>{{ document.storedObject.template.name|localize_translatable_string }}</p> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
|     <div class="item-col"> | ||||
|         <div class="container"> | ||||
|                 <span class="badge-calendar-simple"> | ||||
|                     {{ 'Calendar'|trans }} | ||||
|                     {% if c.endDate.diff(c.startDate).days >= 1 %} | ||||
|                         {{ c.startDate|format_datetime('short', 'short') }} | ||||
|                         - {{ c.endDate|format_datetime('short', 'short') }} | ||||
|                     {% else %} | ||||
|                         {{ c.startDate|format_datetime('short', 'short') }} | ||||
|                         - {{ c.endDate|format_datetime('none', 'short') }} | ||||
|                     {% endif %} | ||||
|                 </span> | ||||
|             </div> | ||||
|  | ||||
|             <div class="denomination h2"> | ||||
|                 {{ document.storedObject.title|chill_print_or_message("No title") }} | ||||
|             </div> | ||||
|             {% if document.storedObject.hasTemplate %} | ||||
|                 <div> | ||||
|                     <p>{{ document.storedObject.template.name|localize_translatable_string }}</p> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|         <div class="aside"> | ||||
|             <div class="dates row text-end"> | ||||
|                 <span>{{ document.storedObject.createdAt|format_date('short') }}</span> | ||||
|             </div> | ||||
|             {% if c.accompanyingPeriod is not null and context == 'person' %} | ||||
|                 <div class="text-end"> | ||||
|                     <span class="badge bg-primary"> | ||||
|                         <i class="fa fa-random"></i> {{ c.accompanyingPeriod.id }} | ||||
|                     </span>  | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -10,6 +10,9 @@ const startApp = ( | ||||
|     collectionEntry: null | HTMLLIElement, | ||||
| ): void => { | ||||
|     console.log("app started", divElement); | ||||
|  | ||||
|     const inputTitle = collectionEntry?.querySelector("input[type='text']"); | ||||
|  | ||||
|     const input_stored_object: HTMLInputElement | null = | ||||
|         divElement.querySelector("input[data-stored-object]"); | ||||
|     if (null === input_stored_object) { | ||||
| @@ -26,9 +29,10 @@ const startApp = ( | ||||
|     const app = createApp({ | ||||
|         template: | ||||
|             '<drop-file-widget :existingDoc="this.$data.existingDoc" :allowRemove="true" @addDocument="this.addDocument" @removeDocument="removeDocument"></drop-file-widget>', | ||||
|         data(vm) { | ||||
|         data() { | ||||
|             return { | ||||
|                 existingDoc: existingDoc, | ||||
|                 inputTitle: inputTitle, | ||||
|             }; | ||||
|         }, | ||||
|         components: { | ||||
| @@ -38,10 +42,13 @@ const startApp = ( | ||||
|             addDocument: function ({ | ||||
|                 stored_object, | ||||
|                 stored_object_version, | ||||
|                 file_name, | ||||
|             }: { | ||||
|                 stored_object: StoredObject; | ||||
|                 stored_object_version: StoredObjectVersion; | ||||
|                 file_name: string; | ||||
|             }): void { | ||||
|                 stored_object.title = file_name; | ||||
|                 console.log("object added", stored_object); | ||||
|                 console.log("version added", stored_object_version); | ||||
|                 this.$data.existingDoc = stored_object; | ||||
| @@ -49,6 +56,11 @@ const startApp = ( | ||||
|                 input_stored_object.value = JSON.stringify( | ||||
|                     this.$data.existingDoc, | ||||
|                 ); | ||||
|                 if (this.$data.inputTitle) { | ||||
|                     if (!this.$data.inputTitle?.value) { | ||||
|                         this.$data.inputTitle.value = file_name; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             removeDocument: function (object: StoredObject): void { | ||||
|                 console.log("catch remove document", object); | ||||
|   | ||||
| @@ -2,26 +2,28 @@ | ||||
|     <teleport to="body"> | ||||
|         <modal v-if="modalOpen" @close="modalOpen = false"> | ||||
|             <template v-slot:header> | ||||
|                 <h2>{{ $t("signature_confirmation") }}</h2> | ||||
|                 <h2>{{ trans(SIGNATURES_SIGNATURE_CONFIRMATION) }}</h2> | ||||
|             </template> | ||||
|             <template v-slot:body> | ||||
|                 <div class="signature-modal-body text-center" v-if="loading"> | ||||
|                     <p>{{ $t("electronic_signature_in_progress") }}</p> | ||||
|                     <p> | ||||
|                         {{ trans(SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS) }} | ||||
|                     </p> | ||||
|                     <div class="loading"> | ||||
|                         <i | ||||
|                             class="fa fa-circle-o-notch fa-spin fa-3x" | ||||
|                             :title="$t('loading')" | ||||
|                             :title="trans(SIGNATURES_LOADING)" | ||||
|                         ></i> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="signature-modal-body text-center" v-else> | ||||
|                     <p>{{ $t("you_are_going_to_sign") }}</p> | ||||
|                     <p>{{ $t("are_you_sure") }}</p> | ||||
|                     <p>{{ trans(SIGNATURES_YOU_ARE_GOING_TO_SIGN) }}</p> | ||||
|                     <p>{{ trans(SIGNATURES_ARE_YOU_SURE) }}</p> | ||||
|                 </div> | ||||
|             </template> | ||||
|             <template v-slot:footer> | ||||
|                 <button class="btn btn-action" @click.prevent="confirmSign"> | ||||
|                     {{ $t("yes") }} | ||||
|                     {{ trans(SIGNATURES_YES) }} | ||||
|                 </button> | ||||
|             </template> | ||||
|         </modal> | ||||
| @@ -82,33 +84,39 @@ | ||||
|                         @change="toggleMultiPage" | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="checkboxMulti"> | ||||
|                         {{ $t("all_pages") }} | ||||
|                         {{ trans(SIGNATURES_ALL_PAGES) }} | ||||
|                     </label> | ||||
|                 </template> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length > 0" | ||||
|                 v-if="signature.zones.length === 1 && signedState !== 'signed'" | ||||
|                 class="col-5 p-0 text-center turnSignature" | ||||
|             > | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone === null || | ||||
|                         userSignatureZone?.index < 1 | ||||
|                     " | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="goToSignatureZoneUnique" | ||||
|                 > | ||||
|                     {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length > 1" | ||||
|                 class="col-5 p-0 text-center turnSignature" | ||||
|             > | ||||
|                 <button | ||||
|                     :disabled="isFirstSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(-1)" | ||||
|                 > | ||||
|                     {{ $t("last_zone") }} | ||||
|                     {{ trans(SIGNATURES_LAST_ZONE) }} | ||||
|                 </button> | ||||
|                 <span>|</span> | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone?.index >= signature.zones.length - 1 | ||||
|                     " | ||||
|                     :disabled="isLastSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(1)" | ||||
|                 > | ||||
|                     {{ $t("next_zone") }} | ||||
|                     {{ trans(SIGNATURES_NEXT_ZONE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="col text-end" v-if="signedState !== 'signed'"> | ||||
| @@ -117,9 +125,9 @@ | ||||
|                     :hidden="!userSignatureZone" | ||||
|                     @click="undoSign" | ||||
|                     v-if="signature.zones.length > 1" | ||||
|                     :title="$t('choose_another_signature')" | ||||
|                     :title="trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE)" | ||||
|                 > | ||||
|                     {{ $t("another_zone") }} | ||||
|                     {{ trans(SIGNATURES_ANOTHER_ZONE) }} | ||||
|                 </button> | ||||
|                 <button | ||||
|                     class="btn btn-misc btn-sm" | ||||
| @@ -127,7 +135,7 @@ | ||||
|                     @click="undoSign" | ||||
|                     v-else | ||||
|                 > | ||||
|                     {{ $t("cancel") }} | ||||
|                     {{ trans(SIGNATURES_CANCEL) }} | ||||
|                 </button> | ||||
|                 <button | ||||
|                     v-if="userSignatureZone === null" | ||||
| @@ -139,7 +147,7 @@ | ||||
|                         active: canvasEvent === 'add', | ||||
|                     }" | ||||
|                     @click="toggleAddZone()" | ||||
|                     :title="$t('add_sign_zone')" | ||||
|                     :title="trans(SIGNATURES_ADD_SIGN_ZONE)" | ||||
|                 > | ||||
|                     <template v-if="canvasEvent === 'add'"> | ||||
|                         <div | ||||
| @@ -191,58 +199,70 @@ | ||||
|                         @change="toggleMultiPage" | ||||
|                     /> | ||||
|                     <label class="form-check-label" for="checkboxMulti"> | ||||
|                         {{ $t("see_all_pages") }} | ||||
|                         {{ trans(SIGNATURES_SEE_ALL_PAGES) }} | ||||
|                     </label> | ||||
|                 </template> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length > 0 && signedState !== 'signed'" | ||||
|                 v-if="signature.zones.length === 1 && signedState !== 'signed'" | ||||
|                 class="col-4 d-xl-none text-center turnSignature p-0" | ||||
|             > | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone === null || | ||||
|                         userSignatureZone?.index < 1 | ||||
|                     " | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(-1)" | ||||
|                     @click="goToSignatureZoneUnique" | ||||
|                 > | ||||
|                     {{ $t("last_zone") }} | ||||
|                 </button> | ||||
|                 <span>|</span> | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone?.index >= signature.zones.length - 1 | ||||
|                     " | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(1)" | ||||
|                 > | ||||
|                     {{ $t("next_zone") }} | ||||
|                     {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length > 0 && signedState !== 'signed'" | ||||
|                 class="col-4 d-none d-xl-flex p-0 text-center turnSignature" | ||||
|                 v-if="signature.zones.length > 1 && signedState !== 'signed'" | ||||
|                 class="col-4 d-xl-none text-center turnSignature p-0" | ||||
|             > | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone === null || | ||||
|                         userSignatureZone?.index < 1 | ||||
|                     " | ||||
|                     :disabled="isFirstSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(-1)" | ||||
|                 > | ||||
|                     {{ $t("last_sign_zone") }} | ||||
|                     {{ trans(SIGNATURES_LAST_ZONE) }} | ||||
|                 </button> | ||||
|                 <span>|</span> | ||||
|                 <button | ||||
|                     :disabled=" | ||||
|                         userSignatureZone?.index >= signature.zones.length - 1 | ||||
|                     " | ||||
|                     :disabled="isLastSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(1)" | ||||
|                 > | ||||
|                     {{ $t("next_sign_zone") }} | ||||
|                     {{ trans(SIGNATURES_NEXT_ZONE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length === 1 && signedState !== 'signed'" | ||||
|                 class="col-4 d-none d-xl-flex p-0 text-center turnSignature" | ||||
|             > | ||||
|                 <button | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="goToSignatureZoneUnique" | ||||
|                 > | ||||
|                     {{ trans(SIGNATURES_GO_TO_SIGNATURE_UNIQUE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div | ||||
|                 v-if="signature.zones.length > 1 && signedState !== 'signed'" | ||||
|                 class="col-4 d-none d-xl-flex p-0 text-center turnSignature" | ||||
|             > | ||||
|                 <button | ||||
|                     :disabled="isFirstSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(-1)" | ||||
|                 > | ||||
|                     {{ trans(SIGNATURES_LAST_SIGN_ZONE) }} | ||||
|                 </button> | ||||
|                 <span>|</span> | ||||
|                 <button | ||||
|                     :disabled="isLastSignatureZone()" | ||||
|                     class="btn btn-light btn-sm" | ||||
|                     @click="turnSignature(1)" | ||||
|                 > | ||||
|                     {{ trans(SIGNATURES_NEXT_SIGN_ZONE) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="col text-end" v-if="signedState !== 'signed'"> | ||||
| @@ -252,7 +272,7 @@ | ||||
|                     @click="undoSign" | ||||
|                     v-if="signature.zones.length > 1" | ||||
|                 > | ||||
|                     {{ $t("choose_another_signature") }} | ||||
|                     {{ trans(SIGNATURES_CHOOSE_ANOTHER_SIGNATURE) }} | ||||
|                 </button> | ||||
|                 <button | ||||
|                     class="btn btn-misc btn-sm" | ||||
| @@ -260,7 +280,7 @@ | ||||
|                     @click="undoSign" | ||||
|                     v-else | ||||
|                 > | ||||
|                     {{ $t("cancel") }} | ||||
|                     {{ trans(SIGNATURES_CANCEL) }} | ||||
|                 </button> | ||||
|                 <button | ||||
|                     v-if="userSignatureZone === null" | ||||
| @@ -272,13 +292,13 @@ | ||||
|                         active: canvasEvent === 'add', | ||||
|                     }" | ||||
|                     @click="toggleAddZone()" | ||||
|                     :title="$t('add_sign_zone')" | ||||
|                     :title="trans(SIGNATURES_ADD_SIGN_ZONE)" | ||||
|                 > | ||||
|                     <template v-if="canvasEvent !== 'add'"> | ||||
|                         {{ $t("add_zone") }} | ||||
|                         {{ trans(SIGNATURES_ADD_ZONE) }} | ||||
|                     </template> | ||||
|                     <template v-else> | ||||
|                         {{ $t("click_on_document") }} | ||||
|                         {{ trans(SIGNATURES_CLICK_ON_DOCUMENT) }} | ||||
|                         <div | ||||
|                             class="spinner-border spinner-border-sm" | ||||
|                             role="status" | ||||
| @@ -312,10 +332,10 @@ | ||||
|                     v-if="signedState !== 'signed'" | ||||
|                     :href="getReturnPath()" | ||||
|                 > | ||||
|                     {{ $t("cancel") }} | ||||
|                     {{ trans(SIGNATURES_CANCEL) }} | ||||
|                 </a> | ||||
|                 <a class="btn btn-misc" v-else :href="getReturnPath()"> | ||||
|                     {{ $t("return") }} | ||||
|                     {{ trans(SIGNATURES_RETURN) }} | ||||
|                 </a> | ||||
|             </div> | ||||
|             <div class="col text-end" v-if="signedState !== 'signed'"> | ||||
| @@ -324,7 +344,7 @@ | ||||
|                     :disabled="!userSignatureZone" | ||||
|                     @click="sign" | ||||
|                 > | ||||
|                     {{ $t("sign") }} | ||||
|                     {{ trans(SIGNATURES_SIGN) }} | ||||
|                 </button> | ||||
|             </div> | ||||
|             <div class="col-4" v-else></div> | ||||
| @@ -333,7 +353,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref, Ref, reactive } from "vue"; | ||||
| import { ref, Ref } from "vue"; | ||||
| import { useToast } from "vue-toast-notification"; | ||||
| import "vue-toast-notification/dist/theme-sugar.css"; | ||||
| import { | ||||
| @@ -344,25 +364,47 @@ import { | ||||
|     SignedState, | ||||
|     ZoomLevel, | ||||
| } from "../../types"; | ||||
| import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; | ||||
| import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; | ||||
| import * as pdfjsLib from "pdfjs-dist"; | ||||
| import { | ||||
|     PDFDocumentProxy, | ||||
|     PDFPageProxy, | ||||
| } from "pdfjs-dist/types/src/display/api"; | ||||
|  | ||||
| // @ts-ignore | ||||
| import { | ||||
|     SIGNATURES_YES, | ||||
|     SIGNATURES_ARE_YOU_SURE, | ||||
|     SIGNATURES_YOU_ARE_GOING_TO_SIGN, | ||||
|     SIGNATURES_SIGNATURE_CONFIRMATION, | ||||
|     SIGNATURES_SIGN, | ||||
|     SIGNATURES_CHOOSE_ANOTHER_SIGNATURE, | ||||
|     SIGNATURES_CANCEL, | ||||
|     SIGNATURES_LAST_SIGN_ZONE, | ||||
|     SIGNATURES_NEXT_SIGN_ZONE, | ||||
|     SIGNATURES_ADD_SIGN_ZONE, | ||||
|     SIGNATURES_CLICK_ON_DOCUMENT, | ||||
|     SIGNATURES_LAST_ZONE, | ||||
|     SIGNATURES_NEXT_ZONE, | ||||
|     SIGNATURES_ADD_ZONE, | ||||
|     SIGNATURES_ANOTHER_ZONE, | ||||
|     SIGNATURES_ELECTRONIC_SIGNATURE_IN_PROGRESS, | ||||
|     SIGNATURES_LOADING, | ||||
|     SIGNATURES_RETURN, | ||||
|     SIGNATURES_SEE_ALL_PAGES, | ||||
|     SIGNATURES_ALL_PAGES, | ||||
|     SIGNATURES_GO_TO_SIGNATURE_UNIQUE, | ||||
|     trans, | ||||
| } from "translator"; | ||||
|  | ||||
| // @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 +475,16 @@ const $toast = useToast(); | ||||
|  | ||||
| const signature = window.signature; | ||||
|  | ||||
| const isFirstSignatureZone = () => | ||||
|     userSignatureZone.value?.index != null | ||||
|         ? userSignatureZone.value.index < 1 | ||||
|         : false; | ||||
|  | ||||
| const isLastSignatureZone = () => | ||||
|     userSignatureZone.value?.index | ||||
|         ? userSignatureZone.value.index >= signature.zones.length - 1 | ||||
|         : false; | ||||
|  | ||||
| const setZoomLevel = async (zoomLevel: string) => { | ||||
|     zoom.value = Number.parseFloat(zoomLevel); | ||||
|     await resetPages(); | ||||
| @@ -604,6 +656,15 @@ const turnPage = async (upOrDown: number) => { | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const selectZoneInCanvas = (signatureZone: SignatureZone) => { | ||||
|     page.value = signatureZone.PDFPage.index + 1; | ||||
|     const canvas = getCanvas(signatureZone.PDFPage.index + 1); | ||||
|     selectZone(signatureZone, canvas); | ||||
|     canvas.scrollIntoView(); | ||||
| }; | ||||
|  | ||||
| const goToSignatureZoneUnique = () => selectZoneInCanvas(signature.zones[0]); | ||||
|  | ||||
| const turnSignature = async (upOrDown: number) => { | ||||
|     let zoneIndex = userSignatureZone.value?.index ?? -1; | ||||
|     if (zoneIndex < -1) { | ||||
| @@ -616,10 +677,7 @@ const turnSignature = async (upOrDown: number) => { | ||||
|     } | ||||
|     let currentZone = signature.zones[zoneIndex]; | ||||
|     if (currentZone) { | ||||
|         page.value = currentZone.PDFPage.index + 1; | ||||
|         const canvas = getCanvas(currentZone.PDFPage.index + 1); | ||||
|         selectZone(currentZone, canvas); | ||||
|         canvas.scrollIntoView(); | ||||
|         selectZoneInCanvas(currentZone); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @@ -754,7 +812,7 @@ const confirmSign = () => { | ||||
|         zone: userSignatureZone.value, | ||||
|     }; | ||||
|     makeFetch("POST", url, body) | ||||
|         .then((r) => { | ||||
|         .then(() => { | ||||
|             checkForReady(); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
| @@ -776,9 +834,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) => { | ||||
|   | ||||
| @@ -23,6 +23,7 @@ const emit = | ||||
|             { | ||||
|                 stored_object_version: StoredObjectVersionCreated, | ||||
|                 stored_object: StoredObject, | ||||
|                 file_name: string, | ||||
|             }, | ||||
|         ) => void | ||||
|     >(); | ||||
| @@ -114,7 +115,21 @@ const handleFile = async (file: File): Promise<void> => { | ||||
|         persisted: false, | ||||
|     }; | ||||
|  | ||||
|     emit("addDocument", { stored_object, stored_object_version }); | ||||
|     const fileName = file.name; | ||||
|     let file_name = "Nouveau document"; | ||||
|     const file_name_split = fileName.split("."); | ||||
|     if (file_name_split.length > 1) { | ||||
|         const extension = file_name_split | ||||
|             ? file_name_split[file_name_split.length - 1] | ||||
|             : ""; | ||||
|         file_name = fileName.replace(extension, "").slice(0, -1); | ||||
|     } | ||||
|  | ||||
|     emit("addDocument", { | ||||
|         stored_object, | ||||
|         stored_object_version, | ||||
|         file_name: file_name, | ||||
|     }); | ||||
|     uploading.value = false; | ||||
| }; | ||||
| </script> | ||||
|   | ||||
| @@ -20,6 +20,7 @@ const emit = defineEmits<{ | ||||
|         { | ||||
|             stored_object: StoredObject, | ||||
|             stored_object_version: StoredObjectVersion, | ||||
|             file_name: string, | ||||
|         }, | ||||
|     ): void; | ||||
|     (e: "removeDocument"): void; | ||||
| @@ -42,14 +43,16 @@ const buttonState = computed<"add" | "replace">(() => { | ||||
| function onAddDocument({ | ||||
|     stored_object, | ||||
|     stored_object_version, | ||||
|     file_name, | ||||
| }: { | ||||
|     stored_object: StoredObject; | ||||
|     stored_object_version: StoredObjectVersion; | ||||
|     file_name: string; | ||||
| }): void { | ||||
|     const message = | ||||
|         buttonState.value === "add" ? "Document ajouté" : "Document remplacé"; | ||||
|     $toast.success(message); | ||||
|     emit("addDocument", { stored_object_version, stored_object }); | ||||
|     emit("addDocument", { stored_object_version, stored_object, file_name }); | ||||
|     state.showModal = false; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ const emit = defineEmits<{ | ||||
|         { | ||||
|             stored_object: StoredObject, | ||||
|             stored_object_version: StoredObjectVersion, | ||||
|             file_name: string, | ||||
|         }, | ||||
|     ): void; | ||||
|     (e: "removeDocument"): void; | ||||
| @@ -53,11 +54,13 @@ const dav_link_href = computed<string | undefined>(() => { | ||||
| const onAddDocument = ({ | ||||
|     stored_object, | ||||
|     stored_object_version, | ||||
|     file_name, | ||||
| }: { | ||||
|     stored_object: StoredObject; | ||||
|     stored_object_version: StoredObjectVersion; | ||||
|     file_name: string; | ||||
| }): void => { | ||||
|     emit("addDocument", { stored_object, stored_object_version }); | ||||
|     emit("addDocument", { stored_object, stored_object_version, file_name }); | ||||
| }; | ||||
|  | ||||
| const onRemoveDocument = (e: Event): void => { | ||||
|   | ||||
| @@ -53,7 +53,7 @@ const onRestored = ({ | ||||
| <template> | ||||
|     <template v-if="props.versions.length > 0"> | ||||
|         <div class="container"> | ||||
|             <template v-for="v in props.versions"> | ||||
|             <template v-for="v in props.versions" :key="v.id"> | ||||
|                 <history-button-list-item | ||||
|                     :version="v" | ||||
|                     :can-edit="canEdit" | ||||
|   | ||||
| @@ -32,13 +32,17 @@ const onRestore = ({ | ||||
|     emit("restoreVersion", { newVersion }); | ||||
| }; | ||||
|  | ||||
| const isKeptBeforeConversion = computed<boolean>(() => | ||||
|     props.version["point-in-times"].reduce( | ||||
|         (accumulator: boolean, pit: StoredObjectPointInTime) => | ||||
|             accumulator || "keep-before-conversion" === pit.reason, | ||||
|         false, | ||||
|     ), | ||||
| ); | ||||
| const isKeptBeforeConversion = computed<boolean>(() => { | ||||
|     if ("point-in-times" in props.version) { | ||||
|         return props.version["point-in-times"].reduce( | ||||
|             (accumulator: boolean, pit: StoredObjectPointInTime) => | ||||
|                 accumulator || "keep-before-conversion" === pit.reason, | ||||
|             false, | ||||
|         ); | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const isRestored = computed<boolean>( | ||||
|     () => props.version.version > 0 && null !== props.version["from-restored"], | ||||
| @@ -90,11 +94,11 @@ const classes = computed<{ | ||||
|         <div class="col-12"> | ||||
|             <file-icon :type="version.type"></file-icon> | ||||
|             <span | ||||
|                 ><strong>#{{ version.version + 1 }}</strong></span | ||||
|                 ><strong> #{{ version.version + 1 }} </strong></span | ||||
|             > | ||||
|             <template | ||||
|                 v-if="version.createdBy !== null && version.createdAt !== null" | ||||
|                 ><strong v-if="version.version == 0">Créé par</strong | ||||
|                 ><strong v-if="version.version == 0">créé par</strong | ||||
|                 ><strong v-else>modifié par</strong> | ||||
|                 <span class="badge-user" | ||||
|                     ><UserRenderBoxBadge | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -23,7 +23,7 @@ License * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| {{ encore_entry_link_tags("mod_document_action_buttons_group") }} | ||||
| {% endblock %} {% block content %} | ||||
|  | ||||
| <div class="col-md-10 col-xxl"> | ||||
| <div class="document-list"> | ||||
|     <h1> | ||||
|         {{ 'Documents for %name%'|trans({ '%name%': person|chill_entity_render_string } ) }} | ||||
|     </h1> | ||||
|   | ||||
| @@ -3,54 +3,56 @@ | ||||
| {% import '@ChillPerson/Macro/updatedBy.html.twig' as mmm %} | ||||
|  | ||||
| <div class="item-row"> | ||||
|     <div class="item-col" style="width: unset"> | ||||
|         {% if document.object.isPending %} | ||||
|             <div class="badge text-bg-info" data-docgen-is-pending="{{ document.object.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|         {% elseif document.object.isFailure %} | ||||
|             <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|         {% endif %} | ||||
|     <!-- person document or accompanying course document --> | ||||
|     <div class="item-two-col-grid"> | ||||
|         <div class="title"> | ||||
|             {% if document.object.isPending %} | ||||
|                 <div class="badge text-bg-info" data-docgen-is-pending="{{ document.object.id }}">{{ 'docgen.Doc generation is pending'|trans }}</div> | ||||
|             {% elseif document.object.isFailure %} | ||||
|                 <div class="badge text-bg-warning">{{ 'docgen.Doc generation failed'|trans }}</div> | ||||
|             {% endif %} | ||||
|  | ||||
|         {% if context == 'person' and accompanyingCourse is defined %} | ||||
|             <div> | ||||
|                     <span class="badge bg-primary"> | ||||
|                         <i class="fa fa-random"></i> {{ accompanyingCourse.id }} | ||||
|                     </span>  | ||||
|             <div class="denomination h2"> | ||||
|                 {{ document.title|chill_print_or_message("No title") }} | ||||
|             </div> | ||||
|         {% elseif context == 'accompanying-period' and person is defined %} | ||||
|             <div> | ||||
|                     <span class="badge bg-primary"> | ||||
|                         {{  'Document from person %name%'|trans({ '%name%': document.person|chill_entity_render_string }) }} | ||||
|                     </span>  | ||||
|             </div> | ||||
|  | ||||
|         {% endif %} | ||||
|         <div class="denomination h2"> | ||||
|             {{ document.title|chill_print_or_message("No title") }} | ||||
|         </div> | ||||
|         {% if document.object.type is not empty %} | ||||
|             <div> | ||||
|                 {{ mm.mimeIcon(document.object.type) }} | ||||
|             </div> | ||||
|         {% endif %} | ||||
|         <div> | ||||
|             <p>{{ document.category.name|localize_translatable_string }}</p> | ||||
|         </div> | ||||
|         {% if document.object.hasTemplate %} | ||||
|             <div> | ||||
|                 <p>{{ document.object.template.name|localize_translatable_string }}</p> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
|     <div class="item-col"> | ||||
|         <div class="container"> | ||||
|             {% if document.date is not null %} | ||||
|                 <div class="dates row text-end"> | ||||
|                     <span>{{ document.date|format_date('short') }}</span> | ||||
|             {% if document.object.type is not empty %} | ||||
|                 <div> | ||||
|                     {{ mm.mimeIcon(document.object.type) }} | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|             {% if document.category %} | ||||
|                 <div> | ||||
|                     <p>{{ document.category.name|localize_translatable_string }}</p> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|             {% if document.object.hasTemplate %} | ||||
|                 <div> | ||||
|                     <p>{{ document.object.template.name|localize_translatable_string }}</p> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|         {% if document.date is not null %} | ||||
|             <div class="aside"> | ||||
|                 <div class="dates row text-end"> | ||||
|                     <span>{{ document.date|format_date('short') }}</span> | ||||
|                 </div> | ||||
|                 {% if context == 'person' and accompanyingCourse is defined %} | ||||
|                     <div class="text-end"> | ||||
|                         <span class="badge bg-primary"> | ||||
|                             <i class="fa fa-random"></i> {{ accompanyingCourse.id }} | ||||
|                         </span>  | ||||
|                     </div> | ||||
|                 {% elseif context == 'accompanying-period' and person is defined %} | ||||
|                     <div class="text-end"> | ||||
|                         <span class="badge bg-primary"> | ||||
|                              {{ document.person|chill_entity_render_string }} | ||||
|                         </span>  | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|             </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|  | ||||
| </div> | ||||
| {% if document.description is not empty %} | ||||
|     <div class="item-row"> | ||||
|   | ||||
| @@ -62,7 +62,15 @@ final readonly class RemoveOldVersionMessageHandler implements MessageHandlerInt | ||||
|  | ||||
|         $storedObject = $storedObjectVersion->getStoredObject(); | ||||
|  | ||||
|         $this->storedObjectManager->delete($storedObjectVersion); | ||||
|         if ($this->storedObjectManager->exists($storedObjectVersion)) { | ||||
|             $this->storedObjectManager->delete($storedObjectVersion); | ||||
|         } else { | ||||
|             $this->logger->notice( | ||||
|                 self::LOG_PREFIX.'Stored object version does not exists any more.', | ||||
|                 ['storedObjectVersionName' => $storedObjectVersion->getFilename()], | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         // to ensure an immediate deletion | ||||
|         $this->entityManager->remove($storedObjectVersion); | ||||
|  | ||||
|   | ||||
| @@ -44,6 +44,7 @@ class RemoveOldVersionMessageHandlerTest extends TestCase | ||||
|         $entityManager->expects($this->once())->method('clear'); | ||||
|  | ||||
|         $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class); | ||||
|         $storedObjectManager->expects($this->once())->method('exists')->willReturn(true); | ||||
|         $storedObjectManager->expects($this->once())->method('delete')->with($this->identicalTo($version)); | ||||
|  | ||||
|         $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock()); | ||||
| @@ -51,6 +52,29 @@ class RemoveOldVersionMessageHandlerTest extends TestCase | ||||
|         $handler(new RemoveOldVersionMessage(1)); | ||||
|     } | ||||
|  | ||||
|     public function testInvokeForVersionNotExisting(): void | ||||
|     { | ||||
|         $object = new StoredObject(); | ||||
|         $version = $object->registerVersion(); | ||||
|         $storedObjectVersionRepository = $this->createMock(StoredObjectVersionRepository::class); | ||||
|         $storedObjectVersionRepository->expects($this->once())->method('find') | ||||
|             ->with($this->identicalTo(1)) | ||||
|             ->willReturn($version); | ||||
|  | ||||
|         $entityManager = $this->createMock(EntityManagerInterface::class); | ||||
|         $entityManager->expects($this->once())->method('remove')->with($this->identicalTo($version)); | ||||
|         $entityManager->expects($this->once())->method('flush'); | ||||
|         $entityManager->expects($this->once())->method('clear'); | ||||
|  | ||||
|         $storedObjectManager = $this->createMock(StoredObjectManagerInterface::class); | ||||
|         $storedObjectManager->expects($this->once())->method('exists')->willReturn(false); | ||||
|         $storedObjectManager->expects($this->never())->method('delete')->with($this->identicalTo($version)); | ||||
|  | ||||
|         $handler = new RemoveOldVersionMessageHandler($storedObjectVersionRepository, new NullLogger(), $entityManager, $storedObjectManager, new MockClock()); | ||||
|  | ||||
|         $handler(new RemoveOldVersionMessage(1)); | ||||
|     } | ||||
|  | ||||
|     public function testInvokeWithStoredObjectToDelete(): void | ||||
|     { | ||||
|         $object = new StoredObject(); | ||||
| @@ -123,6 +147,6 @@ class DummyStoredObjectManager implements StoredObjectManagerInterface | ||||
|  | ||||
|     public function exists(StoredObject|StoredObjectVersion $document): bool | ||||
|     { | ||||
|         throw new \RuntimeException(); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -99,3 +99,30 @@ CHILL_ACCOMPANYING_COURSE_DOCUMENT_UPDATE: Modifier un document | ||||
| entity_display_title: | ||||
|     Document (n°%doc%): "Document (n°%doc%)" | ||||
|     Doc for evaluation (n°%eval%): Document de l'évaluation n°%eval% | ||||
|  | ||||
|  | ||||
| # SIGNATURES | ||||
|  | ||||
| signatures: | ||||
|     yes: Oui | ||||
|     are_you_sure: Êtes-vous sûr·e? | ||||
|     you_are_going_to_sign: Vous allez signer le document | ||||
|     signature_confirmation: Confirmation de la signature | ||||
|     sign: Signer | ||||
|     choose_another_signature: Choisir une autre zone | ||||
|     cancel: Annuler | ||||
|     last_sign_zone: Zone de signature précédente | ||||
|     next_sign_zone: Zone de signature suivante | ||||
|     add_sign_zone: Ajouter une zone de signature | ||||
|     click_on_document: Cliquer sur le document | ||||
|     last_zone: Zone précédente | ||||
|     next_zone: Zone suivante | ||||
|     add_zone: Ajouter une zone | ||||
|     another_zone: Autre zone | ||||
|     electronic_signature_in_progress: Signature électronique en cours... | ||||
|     loading: Chargement... | ||||
|     remove_sign_zone: Enlever la zone | ||||
|     return: Retour | ||||
|     see_all_pages: Voir toutes les pages | ||||
|     all_pages: Toutes les pages | ||||
|     go_to_signature_unique: Aller à la zone de signature | ||||
|   | ||||
| @@ -1,9 +1,5 @@ | ||||
| #events | ||||
| Name: Nom | ||||
| Label: Nom | ||||
| Date: Date | ||||
| Event type : Type d'événement | ||||
| See: Voir | ||||
| Event: Événement | ||||
| Events: Événements | ||||
| 'Event : %label%': Événement "%label%" | ||||
| @@ -123,7 +119,6 @@ Role: Rôles | ||||
| Role creation: Nouveau rôle | ||||
| Role edit: Modifier un rôle | ||||
|  | ||||
| '': '' | ||||
| xlsx: xlsx | ||||
| ods: ods | ||||
| csv: csv | ||||
|   | ||||
| @@ -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'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -18,6 +18,7 @@ use Symfony\Component\Form\AbstractType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\FormType; | ||||
| use Symfony\Component\Form\FormBuilderInterface; | ||||
| use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
| use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | ||||
|  | ||||
| class ExportType extends AbstractType | ||||
| { | ||||
| @@ -29,7 +30,15 @@ class ExportType extends AbstractType | ||||
|  | ||||
|     final public const PICK_FORMATTER_KEY = 'pick_formatter'; | ||||
|  | ||||
|     public function __construct(private readonly ExportManager $exportManager, private readonly SortExportElement $sortExportElement) {} | ||||
|     private array $personFieldsConfig; | ||||
|  | ||||
|     public function __construct( | ||||
|         private readonly ExportManager $exportManager, | ||||
|         private readonly SortExportElement $sortExportElement, | ||||
|         protected ParameterBagInterface $parameterBag, | ||||
|     ) { | ||||
|         $this->personFieldsConfig = $parameterBag->get('chill_person.person_fields'); | ||||
|     } | ||||
|  | ||||
|     public function buildForm(FormBuilderInterface $builder, array $options) | ||||
|     { | ||||
| @@ -77,6 +86,17 @@ class ExportType extends AbstractType | ||||
|             ); | ||||
|  | ||||
|             foreach ($aggregators as $alias => $aggregator) { | ||||
|                 /* | ||||
|                  * eventually mask aggregator | ||||
|                  */ | ||||
|                 if (str_starts_with((string) $alias, 'person_') and str_ends_with((string) $alias, '_aggregator')) { | ||||
|                     $field = preg_replace(['/person_/', '/_aggregator/'], '', (string) $alias); | ||||
|                     if (array_key_exists($field, $this->personFieldsConfig) and 'visible' !== $this->personFieldsConfig[$field]) { | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 $aggregatorBuilder->add($alias, AggregatorType::class, [ | ||||
|                     'aggregator_alias' => $alias, | ||||
|                     'export_manager' => $this->exportManager, | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|   | ||||
| @@ -33,6 +33,8 @@ | ||||
|  | ||||
| @import './scss/hover.scss'; | ||||
|  | ||||
| @import './scss/comment-editor.scss'; | ||||
|  | ||||
| /* | ||||
|  *    BASE LAYOUT POSITION | ||||
|  */ | ||||
|   | ||||
| @@ -0,0 +1,39 @@ | ||||
| .comment-container { | ||||
|     margin-top: 1.5rem; | ||||
| } | ||||
|  | ||||
| .toggle-button { | ||||
|     background-color: white; | ||||
|     font-size: .8rem; | ||||
|     text-decoration: none; | ||||
|     position: absolute; | ||||
|     bottom: -10px; | ||||
|     left: 20px; | ||||
|     padding: 2px 6px; | ||||
|     cursor: pointer; | ||||
|     z-index: 10; | ||||
|     transition: left 0.1s ease-in-out; | ||||
| } | ||||
|  | ||||
| .rich-editor-active .toggle-button { | ||||
|     bottom: 0; | ||||
| } | ||||
|  | ||||
| .editor-wrapper textarea { | ||||
|     resize: vertical; | ||||
|     min-height: 100px; | ||||
| } | ||||
|  | ||||
| .editor-container { | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| .editor-wrapper { | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .hidden-textarea { | ||||
|     display: none; | ||||
| } | ||||
| @@ -25,7 +25,34 @@ div.flex-table { | ||||
|             div.item-col:last-child { | ||||
|                 display: flex; | ||||
|             } | ||||
|  | ||||
|             div.item-two-col-grid { | ||||
|                 display: grid; | ||||
|                 width: 100%; | ||||
|                 justify-content: stretch; | ||||
|  | ||||
|                 @include media-breakpoint-up(lg) { | ||||
|                     grid-template-areas: | ||||
|                         "title aside"; | ||||
|                     grid-template-columns: 1fr minmax(8rem, 1fr); | ||||
|                     column-gap: 0.5em; | ||||
|                 } | ||||
|                 @include media-breakpoint-down(lg) { | ||||
|                     grid-template-areas: | ||||
|                         "aside" | ||||
|                         "title"; | ||||
|                 } | ||||
|  | ||||
|                 & > div.title { | ||||
|                     grid-area: title; | ||||
|                 } | ||||
|  | ||||
|                 & > div.aside { | ||||
|                     grid-area: aside; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     h2, h3, h4, dl, p { | ||||
|   | ||||
| @@ -1,30 +1,32 @@ | ||||
| import ClassicEditorBase from "@ckeditor/ckeditor5-editor-classic/src/classiceditor"; | ||||
| import EssentialsPlugin from "@ckeditor/ckeditor5-essentials/src/essentials"; | ||||
| import MarkdownPlugin from "@ckeditor/ckeditor5-markdown-gfm/src/markdown"; | ||||
| import BoldPlugin from "@ckeditor/ckeditor5-basic-styles/src/bold"; | ||||
| import ItalicPlugin from "@ckeditor/ckeditor5-basic-styles/src/italic"; | ||||
| import BlockQuotePlugin from "@ckeditor/ckeditor5-block-quote/src/blockquote"; | ||||
| import HeadingPlugin from "@ckeditor/ckeditor5-heading/src/heading"; | ||||
| import LinkPlugin from "@ckeditor/ckeditor5-link/src/link"; | ||||
| import ListPlugin from "@ckeditor/ckeditor5-list/src/list"; | ||||
| import ParagraphPlugin from "@ckeditor/ckeditor5-paragraph/src/paragraph"; | ||||
| import { | ||||
|     Essentials, | ||||
|     Bold, | ||||
|     Italic, | ||||
|     Paragraph, | ||||
|     Markdown, | ||||
|     BlockQuote, | ||||
|     Heading, | ||||
|     Link, | ||||
|     List, | ||||
| } from 'ckeditor5'; | ||||
| import coreTranslations from 'ckeditor5/translations/fr.js'; | ||||
|  | ||||
| import 'ckeditor5/ckeditor5.css'; | ||||
|  | ||||
| import "./index.scss"; | ||||
|  | ||||
| export default class ClassicEditor extends ClassicEditorBase {} | ||||
|  | ||||
| ClassicEditor.builtinPlugins = [ | ||||
|     EssentialsPlugin, | ||||
|     MarkdownPlugin, | ||||
|     BoldPlugin, | ||||
|     ItalicPlugin, | ||||
|     BlockQuotePlugin, | ||||
|     HeadingPlugin, | ||||
|     LinkPlugin, | ||||
|     ListPlugin, | ||||
|     ParagraphPlugin, | ||||
| ]; | ||||
|  | ||||
| ClassicEditor.defaultConfig = { | ||||
| export default { | ||||
|     plugins: [ | ||||
|         Essentials, | ||||
|         Markdown, | ||||
|         Bold, | ||||
|         Italic, | ||||
|         BlockQuote, | ||||
|         Heading, | ||||
|         Link, | ||||
|         List, | ||||
|         Paragraph, | ||||
|     ], | ||||
|     toolbar: { | ||||
|         items: [ | ||||
|             "heading", | ||||
| @@ -39,5 +41,8 @@ ClassicEditor.defaultConfig = { | ||||
|             "redo", | ||||
|         ], | ||||
|     }, | ||||
|     language: "fr", | ||||
| }; | ||||
|     translations: [ | ||||
|         coreTranslations | ||||
|     ], | ||||
|     licenseKey: "GPL", | ||||
| } ; | ||||
|   | ||||
| @@ -1,14 +1,23 @@ | ||||
| import ClassicEditor from "./editor_config"; | ||||
| import App from "../../vuejs/CommentEditor/App.vue" | ||||
| import { createApp, reactive } from "vue"; | ||||
|  | ||||
| const ckeditorFields: NodeListOf<HTMLTextAreaElement> = | ||||
|     document.querySelectorAll("textarea[ckeditor]"); | ||||
| ckeditorFields.forEach((field: HTMLTextAreaElement): void => { | ||||
|     ClassicEditor.create(field) | ||||
|         .then((editor) => { | ||||
|             //console.log( 'CkEditor was initialized', editor ); | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|             console.error(error.stack); | ||||
|         }); | ||||
|     document.querySelectorAll("[id^='comment-app']"); | ||||
|  | ||||
| const globalState = reactive({ | ||||
|     isSimple: localStorage.getItem('editorMode') === 'simple' | ||||
| }); | ||||
| window.addEventListener('storage', () => { | ||||
|     globalState.isSimple = localStorage.getItem('editorMode') === 'simple'; | ||||
| }); | ||||
|  | ||||
| ckeditorFields.forEach((field: HTMLTextAreaElement): void => { | ||||
|     const app = createApp(App,{ | ||||
|         fieldName: field.dataset.fieldName, | ||||
|         template: `<app></app>` | ||||
|     }); | ||||
|  | ||||
|     app.provide('globalState', globalState) | ||||
|         .component("app", App) | ||||
|         .mount(field); | ||||
| }); | ||||
| //Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea')); | ||||
|   | ||||
| @@ -1,11 +1,10 @@ | ||||
| import { createApp } from "vue"; | ||||
| import NotificationReadToggle from "ChillMainAssets/vuejs/_components/Notification/NotificationReadToggle.vue"; | ||||
| import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; | ||||
| import NotificationReadAllToggle from "ChillMainAssets/vuejs/_components/Notification/NotificationReadAllToggle.vue"; | ||||
|  | ||||
| const i18n = _createI18n({}); | ||||
|  | ||||
| window.addEventListener("DOMContentLoaded", function (e) { | ||||
| window.addEventListener("DOMContentLoaded", function () { | ||||
|   document | ||||
|     .querySelectorAll(".notification_toggle_read_status") | ||||
|     .forEach(function (el, i) { | ||||
|   | ||||
| @@ -10,6 +10,10 @@ let appsPerInput = new Map(); | ||||
|  | ||||
| function loadDynamicPicker(element) { | ||||
|   let apps = element.querySelectorAll('[data-module="pick-dynamic"]'); | ||||
|   let suggested; | ||||
|   let as_id; | ||||
|   let submit_on_adding_new_entity; | ||||
|   let label; | ||||
|  | ||||
|   apps.forEach(function (el) { | ||||
|     const isMultiple = parseInt(el.dataset.multiple) === 1, | ||||
| @@ -22,12 +26,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 +177,7 @@ document.addEventListener("pick-entity-type-action", function (e) { | ||||
|   } | ||||
| }); | ||||
|  | ||||
| document.addEventListener("DOMContentLoaded", function (e) { | ||||
| document.addEventListener("DOMContentLoaded", function () { | ||||
|   loadDynamicPicker(document); | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| import { createApp } from "vue"; | ||||
| import OpenWopiLink from "ChillMainAssets/vuejs/_components/OpenWopiLink"; | ||||
| import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; | ||||
|  | ||||
| const i18n = _createI18n({}); | ||||
|  | ||||
| //TODO move to chillDocStore or ChillWopi | ||||
|  | ||||
| /* | ||||
|  | ||||
| tags to load module: | ||||
|  | ||||
| <span data-module="wopi-link" | ||||
|     data-wopi-url="{{ path('chill_wopi_file_edit', {'fileId': document.uuid}) }}" | ||||
|     data-doc-type="{{ document.type|e('html_attr') }}" | ||||
|     data-options="{{ options|json_encode }}" | ||||
|         ></span> | ||||
|  | ||||
| */ | ||||
|  | ||||
| window.addEventListener("DOMContentLoaded", function (e) { | ||||
|   document | ||||
|     .querySelectorAll('span[data-module="wopi-link"]') | ||||
|     .forEach(function (el) { | ||||
|       createApp({ | ||||
|         template: | ||||
|           '<open-wopi-link :wopiUrl="wopiUrl" :type="type" :options="options"></open-wopi-link>', | ||||
|         components: { | ||||
|           OpenWopiLink, | ||||
|         }, | ||||
|         data() { | ||||
|           return { | ||||
|             wopiUrl: el.dataset.wopiUrl, | ||||
|             type: el.dataset.docType, | ||||
|             options: | ||||
|               el.dataset.options !== "null" | ||||
|                 ? JSON.parse(el.dataset.options) | ||||
|                 : {}, | ||||
|           }; | ||||
|         }, | ||||
|       }) | ||||
|         .use(i18n) | ||||
|         .mount(el); | ||||
|     }); | ||||
| }); | ||||
| @@ -0,0 +1,36 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <div> | ||||
|             <comment-editor | ||||
|                 :isSimple="globalState.isSimple" | ||||
|                 :fieldName="fieldName" | ||||
|                 @toggle="toggleEditorMode" | ||||
|                 ></comment-editor> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { defineComponent, inject } from 'vue'; | ||||
| import CommentEditor from "../CommentEditor/component/CommentEditor.vue"; | ||||
|  | ||||
| export default defineComponent({ | ||||
|     name: "App", | ||||
|     components: { CommentEditor }, | ||||
|     props: { | ||||
|         fieldName: String | ||||
|     }, | ||||
|     setup() { | ||||
|         const globalState = inject('globalState'); | ||||
|         const toggleEditorMode = () => { | ||||
|             globalState.isSimple = !globalState.isSimple; | ||||
|             localStorage.setItem('editorMode', globalState.isSimple ? 'simple' : 'rich'); | ||||
|         }; | ||||
|  | ||||
|         return { | ||||
|             globalState, | ||||
|             toggleEditorMode, | ||||
|         }; | ||||
|     } | ||||
| }); | ||||
| </script> | ||||
| @@ -0,0 +1,58 @@ | ||||
| <template> | ||||
|     <div :class="{'editor-container': true, 'rich-editor-active': !isSimple}"> | ||||
|         <div v-if="!isSimple" class="editor-wrapper"> | ||||
|             <ckeditor | ||||
|                 :name="fieldName" | ||||
|                 :editor="classicEditor" | ||||
|                 :config="editorConfig" | ||||
|                 v-model.lazy="content" | ||||
|                 tag-name="textarea" | ||||
|             /> | ||||
|         </div> | ||||
|         <div v-else class="editor-wrapper"> | ||||
|             <textarea | ||||
|                 v-model.lazy="content" | ||||
|                 :name="fieldName" | ||||
|                 class="form-control" | ||||
|             ></textarea> | ||||
|         </div> | ||||
|         <a @click="toggleSimpleEditor" class="toggle-button">{{ isSimple ? "rich" : "simple" }}</a> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, ref, computed, toRefs } from 'vue'; | ||||
| import { Ckeditor } from "@ckeditor/ckeditor5-vue"; | ||||
| import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config"; | ||||
| import { ClassicEditor } from "ckeditor5"; | ||||
|  | ||||
| export default defineComponent({ | ||||
|     name: "CommentEditor", | ||||
|     components: { | ||||
|         ckeditor: Ckeditor, | ||||
|     }, | ||||
|     props: { | ||||
|         type: String, | ||||
|         isSimple: Boolean, | ||||
|         fieldName: String, | ||||
|     }, | ||||
|     setup(props, { emit }) { | ||||
|         const { isSimple } = toRefs(props); | ||||
|         const content = ref(""); | ||||
|         const classicEditor = ClassicEditor; | ||||
|         const editorConfig = classicEditorConfig; | ||||
|  | ||||
|         const toggleSimpleEditor = () => { | ||||
|             emit("toggle"); | ||||
|         }; | ||||
|  | ||||
|         return { | ||||
|             isSimple, | ||||
|             content, | ||||
|             classicEditor, | ||||
|             editorConfig, | ||||
|             toggleSimpleEditor, | ||||
|         }; | ||||
|     } | ||||
| }); | ||||
| </script> | ||||
| @@ -0,0 +1,14 @@ | ||||
| import {personMessages} from "ChillPersonAssets/vuejs/_js/i18n"; | ||||
| import {calendarUserSelectorMessages} from "ChillCalendarAssets/vuejs/_components/CalendarUserSelector/js/i18n"; | ||||
| import {activityMessages} from "ChillActivityAssets/vuejs/Activity/i18n"; | ||||
|  | ||||
| const appMessages = { | ||||
|   fr: { | ||||
|     mode: { | ||||
|       simple: "Editeur simple", | ||||
|       rich: "Editeur riche" | ||||
|     } | ||||
|   }, | ||||
| }; | ||||
|  | ||||
| export { appMessages }; | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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"> | ||||
|   | ||||
| @@ -70,7 +70,8 @@ const clickOnAddButton = () => { | ||||
| <style scoped lang="scss"> | ||||
| .item-bloc { | ||||
|     &.isPicked { | ||||
|         background: linear-gradient( | ||||
|         background: | ||||
|             linear-gradient( | ||||
|                 180deg, | ||||
|                 rgba(25, 135, 84, 1) 0px, | ||||
|                 rgba(25, 135, 84, 0) 9px | ||||
|   | ||||
| @@ -1,61 +1,66 @@ | ||||
| <template> | ||||
|     <span v-if="entity.type === 'person'" class="badge rounded-pill bg-person"> | ||||
|         {{ $t("person") }} | ||||
|     <span | ||||
|         v-if="props.entity.type === 'person'" | ||||
|         class="badge rounded-pill bg-person" | ||||
|     > | ||||
|         {{ trans(PERSON) }} | ||||
|     </span> | ||||
|  | ||||
|     <span | ||||
|         v-if="entity.type === 'thirdparty'" | ||||
|         v-if="props.entity.type === 'thirdparty'" | ||||
|         class="badge rounded-pill bg-thirdparty" | ||||
|     > | ||||
|         <template v-if="options.displayLong !== true"> | ||||
|             {{ $t("thirdparty.thirdparty") }} | ||||
|         <template v-if="props.options.displayLong !== true"> | ||||
|             {{ trans(THIRDPARTY) }} | ||||
|         </template> | ||||
|  | ||||
|         <i class="fa fa-fw fa-user" v-if="entity.kind === 'child'" /> | ||||
|         <i class="fa fa-fw fa-user" v-if="props.entity.kind === 'child'" /> | ||||
|         <i | ||||
|             class="fa fa-fw fa-hospital-o" | ||||
|             v-else-if="entity.kind === 'company'" | ||||
|             v-else-if="props.entity.kind === 'company'" | ||||
|         /> | ||||
|         <i class="fa fa-fw fa-user-md" v-else /> | ||||
|  | ||||
|         <template v-if="options.displayLong === true"> | ||||
|             <span v-if="entity.kind === 'child'">{{ | ||||
|                 $t("thirdparty.child") | ||||
|         <template v-if="props.options.displayLong === true"> | ||||
|             <span v-if="props.entity.kind === 'child'">{{ | ||||
|                 trans(THIRDPARTY_CONTACT_OF) | ||||
|             }}</span> | ||||
|             <span v-else-if="entity.kind === 'company'">{{ | ||||
|                 $t("thirdparty.company") | ||||
|             <span v-else-if="props.entity.kind === 'company'">{{ | ||||
|                 trans(THIRDPARTY_A_COMPANY) | ||||
|             }}</span> | ||||
|             <span v-else>{{ $t("thirdparty.contact") }}</span> | ||||
|             <span v-else>{{ trans(THIRDPARTY_A_CONTACT) }}</span> | ||||
|         </template> | ||||
|     </span> | ||||
|  | ||||
|     <span v-if="entity.type === 'user'" class="badge rounded-pill bg-user"> | ||||
|         {{ $t("user") }} | ||||
|     <span | ||||
|         v-if="props.entity.type === 'user'" | ||||
|         class="badge rounded-pill bg-user" | ||||
|     > | ||||
|         {{ trans(ACCEPTED_USERS) }} | ||||
|     </span> | ||||
|  | ||||
|     <span v-if="entity.type === 'household'" class="badge rounded-pill bg-user"> | ||||
|         {{ $t("household") }} | ||||
|     <span | ||||
|         v-if="props.entity.type === 'household'" | ||||
|         class="badge rounded-pill bg-user" | ||||
|     > | ||||
|         {{ trans(HOUSEHOLD) }} | ||||
|     </span> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|     name: "BadgeEntity", | ||||
|     props: ["options", "entity"], | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 person: "Usager", | ||||
|                 thirdparty: { | ||||
|                     thirdparty: "Tiers", | ||||
|                     child: "Personne de contact", | ||||
|                     company: "Personne morale", | ||||
|                     contact: "Personne physique", | ||||
|                 }, | ||||
|                 user: "TMS", | ||||
|                 household: "Ménage", | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| <script setup> | ||||
| import { | ||||
|     trans, | ||||
|     HOUSEHOLD, | ||||
|     ACCEPTED_USERS, | ||||
|     THIRDPARTY_A_CONTACT, | ||||
|     THIRDPARTY_CONTACT_OF, | ||||
|     THIRDPARTY_A_COMPANY, | ||||
|     PERSON, | ||||
|     THIRDPARTY, | ||||
| } from "translator"; | ||||
|  | ||||
| const props = defineProps({ | ||||
|     options: Object, | ||||
|     entity: Object, | ||||
| }); | ||||
| </script> | ||||
|   | ||||
| @@ -66,13 +66,13 @@ | ||||
|         <div v-if="useDatePane === true" class="address-more"> | ||||
|             <div v-if="address.validFrom"> | ||||
|                 <span class="validFrom"> | ||||
|                     <b>{{ $t("validFrom") }}</b | ||||
|                     <b>{{ trans(ADDRESS_VALID_FROM) }}</b | ||||
|                     >: {{ $d(address.validFrom.date) }} | ||||
|                 </span> | ||||
|             </div> | ||||
|             <div v-if="address.validTo"> | ||||
|                 <span class="validTo"> | ||||
|                     <b>{{ $t("validTo") }}</b | ||||
|                     <b>{{ trans(ADDRESS_VALID_TO) }}</b | ||||
|                     >: {{ $d(address.validTo.date) }} | ||||
|                 </span> | ||||
|             </div> | ||||
| @@ -83,6 +83,7 @@ | ||||
| <script> | ||||
| import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue"; | ||||
| import AddressDetailsButton from "ChillMainAssets/vuejs/_components/AddressDetails/AddressDetailsButton.vue"; | ||||
| import { trans, ADDRESS_VALID_FROM, ADDRESS_VALID_TO } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "AddressRenderBox", | ||||
| @@ -107,6 +108,9 @@ export default { | ||||
|             type: Boolean, | ||||
|         }, | ||||
|     }, | ||||
|     setup() { | ||||
|         return { trans, ADDRESS_VALID_FROM, ADDRESS_VALID_TO }; | ||||
|     }, | ||||
|     computed: { | ||||
|         component() { | ||||
|             return this.isMultiline === true ? "div" : "span"; | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|             v-if="!subscriberFinal" | ||||
|             @click="subscribeTo('subscribe', 'final')" | ||||
|         > | ||||
|             <i class="fa fa-check fa-fw" /> | ||||
|             {{ $t("subscribe_final") }} | ||||
|             <i class="fa fa-check fa-fw"></i> | ||||
|             {{ trans(WORKFLOW_SUBSCRIBE_FINAL) }} | ||||
|         </button> | ||||
|         <button | ||||
|             class="btn btn-misc" | ||||
| @@ -15,8 +15,8 @@ | ||||
|             v-if="subscriberFinal" | ||||
|             @click="subscribeTo('unsubscribe', 'final')" | ||||
|         > | ||||
|             <i class="fa fa-times fa-fw" /> | ||||
|             {{ $t("unsubscribe_final") }} | ||||
|             <i class="fa fa-times fa-fw"></i> | ||||
|             {{ trans(WORKFLOW_UNSUBSCRIBE_FINAL) }} | ||||
|         </button> | ||||
|         <button | ||||
|             class="btn btn-misc" | ||||
| @@ -24,8 +24,8 @@ | ||||
|             v-if="!subscriberStep" | ||||
|             @click="subscribeTo('subscribe', 'step')" | ||||
|         > | ||||
|             <i class="fa fa-check fa-fw" /> | ||||
|             {{ $t("subscribe_all_steps") }} | ||||
|             <i class="fa fa-check fa-fw"></i> | ||||
|             {{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }} | ||||
|         </button> | ||||
|         <button | ||||
|             class="btn btn-misc" | ||||
| @@ -33,94 +33,55 @@ | ||||
|             v-if="subscriberStep" | ||||
|             @click="subscribeTo('unsubscribe', 'step')" | ||||
|         > | ||||
|             <i class="fa fa-times fa-fw" /> | ||||
|             {{ $t("unsubscribe_all_steps") }} | ||||
|             <i class="fa fa-times fa-fw"></i> | ||||
|             {{ trans(WORKFLOW_UNSUBSCRIBE_ALL_STEPS) }} | ||||
|         </button> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| <script setup> | ||||
| import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts"; | ||||
| import { defineProps, defineEmits } from "vue"; | ||||
| import { | ||||
|     trans, | ||||
|     WORKFLOW_SUBSCRIBE_FINAL, | ||||
|     WORKFLOW_UNSUBSCRIBE_FINAL, | ||||
|     WORKFLOW_SUBSCRIBE_ALL_STEPS, | ||||
|     WORKFLOW_UNSUBSCRIBE_ALL_STEPS, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "EntityWorkflowVueSubscriber", | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 subscribe_final: "Recevoir une notification à l'étape finale", | ||||
|                 unsubscribe_final: | ||||
|                     "Ne plus recevoir de notification à l'étape finale", | ||||
|                 subscribe_all_steps: | ||||
|                     "Recevoir une notification à chaque étape du suivi", | ||||
|                 unsubscribe_all_steps: | ||||
|                     "Ne plus recevoir de notification à chaque étape du suivi", | ||||
|             }, | ||||
|         }, | ||||
| // props | ||||
| const props = defineProps({ | ||||
|     entityWorkflowId: { | ||||
|         type: Number, | ||||
|         required: true, | ||||
|     }, | ||||
|     props: { | ||||
|         entityWorkflowId: { | ||||
|             type: Number, | ||||
|             required: true, | ||||
|         }, | ||||
|         subscriberStep: { | ||||
|             type: Boolean, | ||||
|             required: true, | ||||
|         }, | ||||
|         subscriberFinal: { | ||||
|             type: Boolean, | ||||
|             required: true, | ||||
|         }, | ||||
|     subscriberStep: { | ||||
|         type: Boolean, | ||||
|         required: true, | ||||
|     }, | ||||
|     emits: ["subscriptionUpdated"], | ||||
|     methods: { | ||||
|         subscribeTo(step, to) { | ||||
|             let params = new URLSearchParams(); | ||||
|             params.set("subscribe", to); | ||||
|     subscriberFinal: { | ||||
|         type: Boolean, | ||||
|         required: true, | ||||
|     }, | ||||
| }); | ||||
|  | ||||
|             const url = | ||||
|                 `/api/1.0/main/workflow/${this.entityWorkflowId}/${step}?` + | ||||
|                 params.toString(); | ||||
| //methods | ||||
| const subscribeTo = (step, to) => { | ||||
|     let params = new URLSearchParams(); | ||||
|     params.set("subscribe", to); | ||||
|  | ||||
|             makeFetch("POST", url).then((response) => { | ||||
|                 this.$emit("subscriptionUpdated", response); | ||||
|             }); | ||||
|         }, | ||||
|     }, | ||||
|     const url = | ||||
|         `/api/1.0/main/workflow/${props.entityWorkflowId}/${step}?` + | ||||
|         params.toString(); | ||||
|  | ||||
|     makeFetch("POST", url).then((response) => { | ||||
|         emit("subscriptionUpdated", response); | ||||
|     }); | ||||
| }; | ||||
| /* | ||||
| *   ALTERNATIVES | ||||
| * | ||||
|     <div class="form-check form-switch"> | ||||
|         <input class="form-check-input" type="checkbox" role="switch" id="laststep"> | ||||
|         <label class="form-check-label" for="laststep">{{ $t('subscribe_final') }}</label> | ||||
|     </div> | ||||
|     <div class="form-check form-switch"> | ||||
|         <input class="form-check-input" type="checkbox" role="switch" id="allsteps"> | ||||
|         <label class="form-check-label" for="allsteps">{{ $t('subscribe_all_steps') }}</label> | ||||
|     </div> | ||||
|  | ||||
|     <div class="list-group my-3"> | ||||
|         <label class="list-group-item"> | ||||
|             <input class="form-check-input me-1" type="checkbox" value=""> | ||||
|             {{ $t('subscribe_final') }} | ||||
|         </label> | ||||
|         <label class="list-group-item"> | ||||
|             <input class="form-check-input me-1" type="checkbox" value=""> | ||||
|             {{ $t('subscribe_all_steps') }} | ||||
|         </label> | ||||
|     </div> | ||||
|  | ||||
|     <div class="btn-group-vertical my-3" role="group"> | ||||
|         <button type="button" class="btn btn-outline-primary"> | ||||
|             <i class="fa fa-check fa-fw"></i> | ||||
|             {{ $t('subscribe_final') }} | ||||
|         </button> | ||||
|         <button type="button" class="btn btn-outline-primary"> | ||||
|             <i class="fa fa-check fa-fw"></i> | ||||
|             {{ $t('subscribe_all_steps') }} | ||||
|         </button> | ||||
|     </div> | ||||
| */ | ||||
| // emit | ||||
| const emit = defineEmits(["subscriptionUpdated"]); | ||||
| </script> | ||||
|  | ||||
| <style scoped></style> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
|     <div class="flex-table workflow" id="workflow-list"> | ||||
|         <div | ||||
|             v-for="(w, i) in workflows" | ||||
|             v-for="(w, i) in props.workflows" | ||||
|             :key="`workflow-${i}`" | ||||
|             class="item-bloc" | ||||
|         > | ||||
| @@ -48,7 +48,7 @@ | ||||
|                 <span | ||||
|                     v-if="w.isOnHoldAtCurrentStep" | ||||
|                     class="badge bg-success rounded-pill" | ||||
|                     >{{ $t("on_hold") }}</span | ||||
|                     >{{ trans(WORKFLOW_ON_HOLD) }}</span | ||||
|                 > | ||||
|             </div> | ||||
|  | ||||
| @@ -56,11 +56,11 @@ | ||||
|                 <div class="item-col flex-grow-1"> | ||||
|                     <p v-if="isUserSubscribedToStep(w)"> | ||||
|                         <i class="fa fa-check fa-fw"></i> | ||||
|                         {{ $t("you_subscribed_to_all_steps") }} | ||||
|                         {{ trans(WORKFLOW_YOU_SUBSCRIBED_TO_ALL_STEPS) }} | ||||
|                     </p> | ||||
|                     <p v-if="isUserSubscribedToFinal(w)"> | ||||
|                         <i class="fa fa-check fa-fw"></i> | ||||
|                         {{ $t("you_subscribed_to_final_step") }} | ||||
|                         {{ trans(WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP) }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|                 <div class="item-col"> | ||||
| @@ -69,7 +69,7 @@ | ||||
|                             <a | ||||
|                                 :href="goToUrl(w)" | ||||
|                                 class="btn btn-sm btn-show" | ||||
|                                 :title="$t('action.show')" | ||||
|                                 :title="trans(SEE)" | ||||
|                             ></a> | ||||
|                         </li> | ||||
|                     </ul> | ||||
| @@ -79,85 +79,65 @@ | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| <script setup> | ||||
| import Popover from "bootstrap/js/src/popover"; | ||||
| import { onMounted } from "vue"; | ||||
| import { | ||||
|     trans, | ||||
|     BY_USER, | ||||
|     SEE, | ||||
|     WORKFLOW_YOU_SUBSCRIBED_TO_ALL_STEPS, | ||||
|     WORKFLOW_YOU_SUBSCRIBED_TO_FINAL_STEP, | ||||
|     WORKFLOW_ON_HOLD, | ||||
|     WORKFLOW_AT, | ||||
| } from "translator"; | ||||
|  | ||||
| const i18n = { | ||||
|     messages: { | ||||
|         fr: { | ||||
|             you_subscribed_to_all_steps: | ||||
|                 "Vous recevrez une notification à chaque étape", | ||||
|             you_subscribed_to_final_step: | ||||
|                 "Vous recevrez une notification à l'étape finale", | ||||
|             by: "Par", | ||||
|             at: "Le", | ||||
|             on_hold: "En attente", | ||||
|         }, | ||||
| // props | ||||
| const props = defineProps({ | ||||
|     workflows: { | ||||
|         type: Array, | ||||
|         required: true, | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| // methods | ||||
| const goToUrl = (w) => `/fr/main/workflow/${w.id}/show`; | ||||
| const getPopTitle = (step) => { | ||||
|     if (step.transitionPrevious != null) { | ||||
|         //console.log(step.transitionPrevious.text); | ||||
|         let freezed = step.isFreezed | ||||
|             ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` | ||||
|             : ``; | ||||
|         return `${freezed}${step.transitionPrevious.text}`; | ||||
|     } | ||||
| }; | ||||
| const getPopContent = (step) => { | ||||
|     if (step.transitionPrevious != null) { | ||||
|         if (step.transitionPreviousBy !== null) { | ||||
|             return `<ul class="small_in_title"> | ||||
|               <li><span class="item-key">${trans(BY_USER)} : </span><b>${step.transitionPreviousBy.text}</b></li> | ||||
|               <li><span class="item-key">${trans(WORKFLOW_AT)} : </span><b>${formatDate(step.transitionPreviousAt.datetime)}</b></li> | ||||
|               </ul>`; | ||||
|         } else { | ||||
|             return `<ul class="small_in_title"> | ||||
|                       <li><span class="item-key">${trans(WORKFLOW_AT)} : </span><b>${formatDate(step.transitionPreviousAt.datetime)}</b></li> | ||||
|                   </ul>`; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| const formatDate = (datetime) => | ||||
|     datetime.split("T")[0] + " " + datetime.split("T")[1].substring(0, 5); | ||||
| const isUserSubscribedToStep = () => false; | ||||
| const isUserSubscribedToFinal = () => false; | ||||
|  | ||||
| export default { | ||||
|     name: "ListWorkflow", | ||||
|     i18n: i18n, | ||||
|     props: { | ||||
|         workflows: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|         }, | ||||
|     }, | ||||
|     methods: { | ||||
|         goToUrl(w) { | ||||
|             return `/fr/main/workflow/${w.id}/show`; | ||||
|         }, | ||||
|         getPopTitle(step) { | ||||
|             if (step.transitionPrevious != null) { | ||||
|                 //console.log(step.transitionPrevious.text); | ||||
|                 let freezed = step.isFreezed | ||||
|                     ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` | ||||
|                     : ``; | ||||
|                 return `${freezed}${step.transitionPrevious.text}`; | ||||
|             } | ||||
|         }, | ||||
|         getPopContent(step) { | ||||
|             if (step.transitionPrevious != null) { | ||||
|                 if (step.transitionPreviousBy !== null) { | ||||
|                     return `<ul class="small_in_title"> | ||||
|                       <li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li> | ||||
|                       <li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li> | ||||
|                       </ul>`; | ||||
|                 } else { | ||||
|                     return `<ul class="small_in_title"> | ||||
|                       <li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li> | ||||
|                       </ul>`; | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         formatDate(datetime) { | ||||
|             return ( | ||||
|                 datetime.split("T")[0] + | ||||
|                 " " + | ||||
|                 datetime.split("T")[1].substring(0, 5) | ||||
|             ); | ||||
|         }, | ||||
|         isUserSubscribedToStep(w) { | ||||
|             // todo | ||||
|             return false; | ||||
|         }, | ||||
|         isUserSubscribedToFinal(w) { | ||||
|             // todo | ||||
|             return false; | ||||
|         }, | ||||
|     }, | ||||
|     mounted() { | ||||
|         const triggerList = [].slice.call( | ||||
|             document.querySelectorAll('[data-bs-toggle="popover"]'), | ||||
|         ); | ||||
|         const popoverList = triggerList.map(function (el) { | ||||
|             //console.log('popover', el) | ||||
|             return new Popover(el, { | ||||
|                 html: true, | ||||
|             }); | ||||
| onMounted(() => { | ||||
|     const triggerList = [].slice.call( | ||||
|         document.querySelectorAll('[data-bs-toggle="popover"]'), | ||||
|     ); | ||||
|     triggerList.map(function (el) { | ||||
|         return new Popover(el, { | ||||
|             html: true, | ||||
|         }); | ||||
|     }, | ||||
| }; | ||||
|     }); | ||||
| }); | ||||
| </script> | ||||
|   | ||||
| @@ -1,23 +1,24 @@ | ||||
| <template> | ||||
|     <pick-workflow | ||||
|         :relatedEntityClass="this.relatedEntityClass" | ||||
|         :relatedEntityId="this.relatedEntityId" | ||||
|         :workflowsAvailables="workflowsAvailables" | ||||
|         :preventDefaultMoveToGenerate="this.$props.preventDefaultMoveToGenerate" | ||||
|         :goToGenerateWorkflowPayload="this.goToGenerateWorkflowPayload" | ||||
|     <Pick-workflow | ||||
|         :relatedEntityClass="props.relatedEntityClass" | ||||
|         :relatedEntityId="props.relatedEntityId" | ||||
|         :workflowsAvailables="props.workflowsAvailables" | ||||
|         :preventDefaultMoveToGenerate="props.preventDefaultMoveToGenerate" | ||||
|         :goToGenerateWorkflowPayload="props.goToGenerateWorkflowPayload" | ||||
|         :countExistingWorkflows="countWorkflows" | ||||
|         :embedded-within-list-modal="false" | ||||
|         @go-to-generate-workflow="goToGenerateWorkflow" | ||||
|         @click-open-list="openModal" | ||||
|     ></pick-workflow> | ||||
|     ></Pick-workflow> | ||||
|  | ||||
|     <teleport to="body"> | ||||
|         <modal | ||||
|         <Modal | ||||
|             v-if="modal.showModal" | ||||
|             :modalDialogClass="modal.modalDialogClass" | ||||
|             @close="modal.showModal = false" | ||||
|         > | ||||
|             <template v-slot:header> | ||||
|                 <h2 class="modal-title">{{ $t("workflow_list") }}</h2> | ||||
|                 <h2 class="modal-title">{{ trans(WORKFLOW_LIST) }}</h2> | ||||
|             </template> | ||||
|  | ||||
|             <template v-slot:body> | ||||
| @@ -27,103 +28,80 @@ | ||||
|             <template v-slot:footer> | ||||
|                 <pick-workflow | ||||
|                     v-if="allowCreate" | ||||
|                     :relatedEntityClass="this.relatedEntityClass" | ||||
|                     :relatedEntityId="this.relatedEntityId" | ||||
|                     :workflowsAvailables="workflowsAvailables" | ||||
|                     :relatedEntityClass="props.relatedEntityClass" | ||||
|                     :relatedEntityId="props.relatedEntityId" | ||||
|                     :workflowsAvailables="props.workflowsAvailables" | ||||
|                     :preventDefaultMoveToGenerate=" | ||||
|                         this.$props.preventDefaultMoveToGenerate | ||||
|                         props.preventDefaultMoveToGenerate | ||||
|                     " | ||||
|                     :goToGenerateWorkflowPayload=" | ||||
|                         this.goToGenerateWorkflowPayload | ||||
|                         props.goToGenerateWorkflowPayload | ||||
|                     " | ||||
|                     :countExistingWorkflows="countWorkflows" | ||||
|                     :embedded-within-list-modal="true" | ||||
|                     @go-to-generate-workflow="this.goToGenerateWorkflow" | ||||
|                     @go-to-generate-workflow="goToGenerateWorkflow" | ||||
|                 ></pick-workflow> | ||||
|             </template> | ||||
|         </modal> | ||||
|         </Modal> | ||||
|     </teleport> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| <script setup> | ||||
| import { ref, computed, defineProps, defineEmits } from "vue"; | ||||
| import Modal from "ChillMainAssets/vuejs/_components/Modal"; | ||||
| import PickWorkflow from "ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue"; | ||||
| import ListWorkflowVue from "ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue"; | ||||
| import { trans, WORKFLOW_LIST } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "ListWorkflowModal", | ||||
|     components: { | ||||
|         Modal, | ||||
|         PickWorkflow, | ||||
|         ListWorkflowVue, | ||||
| // Define props | ||||
| const props = defineProps({ | ||||
|     workflows: { | ||||
|         type: Array, | ||||
|         required: true, | ||||
|     }, | ||||
|     emits: ["goToGenerateWorkflow"], | ||||
|     props: { | ||||
|         workflows: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|         }, | ||||
|         allowCreate: { | ||||
|             type: Boolean, | ||||
|             required: true, | ||||
|         }, | ||||
|         relatedEntityClass: { | ||||
|             type: String, | ||||
|             required: true, | ||||
|         }, | ||||
|         relatedEntityId: { | ||||
|             type: Number, | ||||
|             required: false, | ||||
|         }, | ||||
|         workflowsAvailables: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|         }, | ||||
|         preventDefaultMoveToGenerate: { | ||||
|             type: Boolean, | ||||
|             required: false, | ||||
|             default: false, | ||||
|         }, | ||||
|         goToGenerateWorkflowPayload: { | ||||
|             required: false, | ||||
|             default: {}, | ||||
|         }, | ||||
|     allowCreate: { | ||||
|         type: Boolean, | ||||
|         required: true, | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             modal: { | ||||
|                 showModal: false, | ||||
|                 modalDialogClass: "modal-dialog-scrollable modal-xl", | ||||
|             }, | ||||
|         }; | ||||
|     relatedEntityClass: { | ||||
|         type: String, | ||||
|         required: true, | ||||
|     }, | ||||
|     computed: { | ||||
|         countWorkflows() { | ||||
|             return this.workflows.length; | ||||
|         }, | ||||
|         hasWorkflow() { | ||||
|             return this.countWorkflows > 0; | ||||
|         }, | ||||
|     relatedEntityId: { | ||||
|         type: Number, | ||||
|         required: false, | ||||
|     }, | ||||
|     methods: { | ||||
|         openModal() { | ||||
|             this.modal.showModal = true; | ||||
|         }, | ||||
|         goToGenerateWorkflow(data) { | ||||
|             console.log("go to generate workflow intercepted", data); | ||||
|             this.$emit("goToGenerateWorkflow", data); | ||||
|         }, | ||||
|     workflowsAvailables: { | ||||
|         type: Array, | ||||
|         required: true, | ||||
|     }, | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 workflow_list: "Liste des workflows associés", | ||||
|                 workflow: " workflow associé", | ||||
|                 workflows: " workflows associés", | ||||
|             }, | ||||
|         }, | ||||
|     preventDefaultMoveToGenerate: { | ||||
|         type: Boolean, | ||||
|         required: false, | ||||
|         default: false, | ||||
|     }, | ||||
| }; | ||||
|     goToGenerateWorkflowPayload: { | ||||
|         required: false, | ||||
|         default: () => ({}), | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| // Define emits | ||||
| const emit = defineEmits(["goToGenerateWorkflow"]); | ||||
|  | ||||
| // Reactive data | ||||
| const modal = ref({ | ||||
|     showModal: false, | ||||
|     modalDialogClass: "modal-dialog-scrollable modal-xl", | ||||
| }); | ||||
|  | ||||
| // Computed properties | ||||
| const countWorkflows = computed(() => props.workflows.length); | ||||
|  | ||||
| // Methods | ||||
| const openModal = () => (modal.value.showModal = true); | ||||
|  | ||||
| const goToGenerateWorkflow = (data) => emit("goToGenerateWorkflow", data); | ||||
| </script> | ||||
|  | ||||
| <style scoped></style> | ||||
|   | ||||
| @@ -8,28 +8,28 @@ | ||||
|                 aria-modal="true" | ||||
|                 role="dialog" | ||||
|             > | ||||
|                 <div class="modal-dialog" :class="modalDialogClass"> | ||||
|                 <div class="modal-dialog" :class="props.modalDialogClass || {}"> | ||||
|                     <div class="modal-content"> | ||||
|                         <div class="modal-header"> | ||||
|                             <slot name="header" /> | ||||
|                             <button class="close btn" @click="$emit('close')"> | ||||
|                                 <i class="fa fa-times" aria-hidden="true" /> | ||||
|                             <slot name="header"></slot> | ||||
|                             <button class="close btn" @click="emits('close')"> | ||||
|                                 <i class="fa fa-times" aria-hidden="true"></i> | ||||
|                             </button> | ||||
|                         </div> | ||||
|                         <div class="modal-body"> | ||||
|                             <div class="body-head"> | ||||
|                                 <slot name="body-head" /> | ||||
|                                 <slot name="body-head"></slot> | ||||
|                             </div> | ||||
|                             <slot name="body" /> | ||||
|                             <slot name="body"></slot> | ||||
|                         </div> | ||||
|                         <div class="modal-footer" v-if="!hideFooter"> | ||||
|                             <button | ||||
|                                 class="btn btn-cancel" | ||||
|                                 @click="$emit('close')" | ||||
|                                 @click="emits('close')" | ||||
|                             > | ||||
|                                 {{ $t("action.close") }} | ||||
|                                 {{ trans(MODAL_ACTION_CLOSE) }} | ||||
|                             </button> | ||||
|                             <slot name="footer" /> | ||||
|                             <slot name="footer"></slot> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
| @@ -39,8 +39,7 @@ | ||||
|     </transition> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from "vue"; | ||||
| <script lang="ts" setup> | ||||
| /* | ||||
|  *    This Modal component is a mix between Vue3 modal implementation | ||||
|  *    [+] with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden | ||||
| @@ -50,22 +49,23 @@ import { defineComponent } from "vue"; | ||||
|  *    [+] using bootstrap css classes, the modal have a responsive behaviour, | ||||
|  *    [+] modal design can be configured using css classes (size, scroll) | ||||
|  */ | ||||
| export default defineComponent({ | ||||
|     name: "Modal", | ||||
|     props: { | ||||
|         modalDialogClass: { | ||||
|             type: Object, | ||||
|             required: false, | ||||
|             default: {}, | ||||
|         }, | ||||
|         hideFooter: { | ||||
|             type: Boolean, | ||||
|             required: false, | ||||
|             default: false, | ||||
|         }, | ||||
|     }, | ||||
|     emits: ["close"], | ||||
| import { trans, MODAL_ACTION_CLOSE } from "translator"; | ||||
| import { defineProps } from "vue"; | ||||
|  | ||||
| export interface ModalProps { | ||||
|     modalDialogClass: object | null; | ||||
|     hideFooter: boolean; | ||||
| } | ||||
|  | ||||
| // Define the props | ||||
| const props = withDefaults(defineProps<ModalProps>(), { | ||||
|     hideFooter: false, | ||||
|     modalDialogClass: null, | ||||
| }); | ||||
|  | ||||
| const emits = defineEmits<{ | ||||
|     close: []; | ||||
| }>(); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
|   | ||||
| @@ -9,12 +9,12 @@ | ||||
|             class="btn" | ||||
|             :class="overrideClass" | ||||
|             type="button" | ||||
|             :title="$t('markAsUnread')" | ||||
|             :title="trans(NOTIFICATION_MARK_AS_UNREAD)" | ||||
|             @click="markAsUnread" | ||||
|         > | ||||
|             <i class="fa fa-sm fa-envelope-o" /> | ||||
|             <span v-if="!buttonNoText" class="ps-2"> | ||||
|                 {{ $t("markAsUnread") }} | ||||
|             <i class="fa fa-sm fa-envelope-o"></i> | ||||
|             <span v-if="!props.buttonNoText" class="ps-2"> | ||||
|                 {{ trans(NOTIFICATION_MARK_AS_UNREAD) }} | ||||
|             </span> | ||||
|         </button> | ||||
|  | ||||
| @@ -23,12 +23,12 @@ | ||||
|             class="btn" | ||||
|             :class="overrideClass" | ||||
|             type="button" | ||||
|             :title="$t('markAsRead')" | ||||
|             :title="trans(NOTIFICATION_MARK_AS_READ)" | ||||
|             @click="markAsRead" | ||||
|         > | ||||
|             <i class="fa fa-sm fa-envelope-open-o" /> | ||||
|             <i class="fa fa-sm fa-envelope-open-o"></i> | ||||
|             <span v-if="!buttonNoText" class="ps-2"> | ||||
|                 {{ $t("markAsRead") }} | ||||
|                 {{ trans(NOTIFICATION_MARK_AS_READ) }} | ||||
|             </span> | ||||
|         </button> | ||||
|  | ||||
| @@ -37,9 +37,9 @@ | ||||
|             type="button" | ||||
|             class="btn btn-outline-primary" | ||||
|             :href="showUrl" | ||||
|             :title="$t('action.show')" | ||||
|             :title="trans(SEE)" | ||||
|         > | ||||
|             <i class="fa fa-sm fa-comment-o" /> | ||||
|             <i class="fa fa-sm fa-comment-o"></i> | ||||
|         </a> | ||||
|  | ||||
|         <!-- "Mark All Read" button --> | ||||
| @@ -51,7 +51,7 @@ | ||||
|             :title="$t('markAllRead')" | ||||
|             @click="markAllRead" | ||||
|         > | ||||
|             <i class="fa fa-sm fa-envelope-o" /> | ||||
|             <i class="fa fa-sm fa-envelope-o"></i> | ||||
|             <span v-if="!buttonNoText" class="ps-2"> | ||||
|                 {{ $t("markAllRead") }} | ||||
|             </span> | ||||
| @@ -59,89 +59,66 @@ | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| <script setup> | ||||
| import { computed } from "vue"; | ||||
| import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts"; | ||||
| import { | ||||
|     trans, | ||||
|     NOTIFICATION_MARK_AS_READ, | ||||
|     NOTIFICATION_MARK_AS_UNREAD, | ||||
|     SEE, | ||||
| } from "translator"; | ||||
|  | ||||
| export default { | ||||
|     name: "NotificationReadToggle", | ||||
|     props: { | ||||
|         isRead: { | ||||
|             required: true, | ||||
|             type: Boolean, | ||||
|         }, | ||||
|         notificationId: { | ||||
|             required: true, | ||||
|             type: Number, | ||||
|         }, | ||||
|         // Optional | ||||
|         buttonClass: { | ||||
|             required: false, | ||||
|             type: String, | ||||
|         }, | ||||
|         buttonNoText: { | ||||
|             required: false, | ||||
|             type: Boolean, | ||||
|         }, | ||||
|         showUrl: { | ||||
|             required: false, | ||||
|             type: String, | ||||
|         }, | ||||
| // Props | ||||
| const props = defineProps({ | ||||
|     isRead: { | ||||
|         type: Boolean, | ||||
|         required: true, | ||||
|     }, | ||||
|     emits: ["markRead", "markUnread"], | ||||
|     computed: { | ||||
|         /// [Option] override default button appearance (btn-misc) | ||||
|         overrideClass() { | ||||
|             return this.buttonClass ? this.buttonClass : "btn-misc"; | ||||
|         }, | ||||
|         /// [Option] don't display text on button | ||||
|         buttonHideText() { | ||||
|             return this.buttonNoText; | ||||
|         }, | ||||
|         /// [Option] showUrl is href for show page second button. | ||||
|         //  When passed, the component return a button-group with 2 buttons. | ||||
|         isButtonGroup() { | ||||
|             return this.showUrl; | ||||
|         }, | ||||
|     notificationId: { | ||||
|         type: Number, | ||||
|         required: true, | ||||
|     }, | ||||
|     methods: { | ||||
|         markAsUnread() { | ||||
|             makeFetch( | ||||
|                 "POST", | ||||
|                 `/api/1.0/main/notification/${this.notificationId}/mark/unread`, | ||||
|                 [], | ||||
|             ).then(() => { | ||||
|                 this.$emit("markRead", { notificationId: this.notificationId }); | ||||
|             }); | ||||
|         }, | ||||
|         markAsRead() { | ||||
|             makeFetch( | ||||
|                 "POST", | ||||
|                 `/api/1.0/main/notification/${this.notificationId}/mark/read`, | ||||
|                 [], | ||||
|             ).then(() => { | ||||
|                 this.$emit("markUnread", { | ||||
|                     notificationId: this.notificationId, | ||||
|                 }); | ||||
|             }); | ||||
|         }, | ||||
|         markAllRead() { | ||||
|             makeFetch( | ||||
|                 "POST", | ||||
|                 `/api/1.0/main/notification/markallread`, | ||||
|                 [], | ||||
|             ).then(() => { | ||||
|                 this.$emit("markAllRead"); | ||||
|             }); | ||||
|         }, | ||||
|     buttonClass: { | ||||
|         type: String, | ||||
|         required: false, | ||||
|     }, | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 markAsUnread: "Marquer comme non-lu", | ||||
|                 markAsRead: "Marquer comme lu", | ||||
|             }, | ||||
|         }, | ||||
|     buttonNoText: { | ||||
|         type: Boolean, | ||||
|         required: false, | ||||
|     }, | ||||
|     showUrl: { | ||||
|         type: String, | ||||
|         required: false, | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| // Emits | ||||
| const emit = defineEmits(["markRead", "markUnread"]); | ||||
|  | ||||
| // Computed | ||||
| const overrideClass = computed(() => props.buttonClass || "btn-misc"); | ||||
| const isButtonGroup = computed(() => props.showUrl); | ||||
|  | ||||
| // Methods | ||||
| const markAsUnread = () => { | ||||
|     makeFetch( | ||||
|         "POST", | ||||
|         `/api/1.0/main/notification/${props.notificationId}/mark/unread`, | ||||
|         [], | ||||
|     ).then(() => { | ||||
|         emit("markRead", { notificationId: props.notificationId }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const markAsRead = () => { | ||||
|     makeFetch( | ||||
|         "POST", | ||||
|         `/api/1.0/main/notification/${props.notificationId}/mark/read`, | ||||
|         [], | ||||
|     ).then(() => { | ||||
|         emit("markUnread", { notificationId: props.notificationId }); | ||||
|     }); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
|   | ||||
| @@ -1,253 +0,0 @@ | ||||
| <template> | ||||
|     <a | ||||
|         v-if="isOpenDocument" | ||||
|         class="btn" | ||||
|         :class="[ | ||||
|             isChangeIcon ? 'change-icon' : '', | ||||
|             isChangeClass ? options.changeClass : 'btn-wopilink', | ||||
|         ]" | ||||
|         @click="openModal" | ||||
|     > | ||||
|         <i v-if="isChangeIcon" class="fa me-2" :class="options.changeIcon" /> | ||||
|  | ||||
|         <span v-if="!noText"> | ||||
|             {{ $t("online_edit_document") }} | ||||
|         </span> | ||||
|     </a> | ||||
|  | ||||
|     <teleport to="body"> | ||||
|         <div class="wopi-frame" v-if="isOpenDocument"> | ||||
|             <modal | ||||
|                 v-if="modal.showModal" | ||||
|                 :modal-dialog-class="modal.modalDialogClass" | ||||
|                 :hide-footer="true" | ||||
|                 @close="modal.showModal = false" | ||||
|             > | ||||
|                 <template #header> | ||||
|                     <img class="logo" :src="logo" height="45" /> | ||||
|                     <span class="ms-auto me-3"> | ||||
|                         <span v-if="options.title">{{ options.title }}</span> | ||||
|                     </span> | ||||
|                     <!-- | ||||
|                   <a class="btn btn-outline-light"> | ||||
|                      <i class="fa fa-save fa-fw"></i> | ||||
|                      {{ $t('save_and_quit') }} | ||||
|                   </a> | ||||
|                   --> | ||||
|                 </template> | ||||
|  | ||||
|                 <template #body> | ||||
|                     <div v-if="loading" class="loading"> | ||||
|                         <i | ||||
|                             class="fa fa-circle-o-notch fa-spin fa-3x" | ||||
|                             :title="$t('loading')" | ||||
|                         /> | ||||
|                     </div> | ||||
|                     <iframe :src="this.wopiUrl" @load="loaded" /> | ||||
|                 </template> | ||||
|             </modal> | ||||
|         </div> | ||||
|         <div v-else> | ||||
|             <modal | ||||
|                 v-if="modal.showModal" | ||||
|                 modal-dialog-class="modal-sm" | ||||
|                 @close="modal.showModal = false" | ||||
|             > | ||||
|                 <template #header> | ||||
|                     <h3>{{ $t("invalid_title") }}</h3> | ||||
|                 </template> | ||||
|                 <template #body> | ||||
|                     <div class="alert alert-warning"> | ||||
|                         {{ $t("invalid_message") }} | ||||
|                     </div> | ||||
|                 </template> | ||||
|             </modal> | ||||
|         </div> | ||||
|     </teleport> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import Modal from "ChillMainAssets/vuejs/_components/Modal"; | ||||
| import logo from "ChillMainAssets/chill/img/logo-chill-sans-slogan_white.png"; | ||||
|  | ||||
| export default { | ||||
|     name: "OpenWopiLink", | ||||
|     components: { | ||||
|         Modal, | ||||
|     }, | ||||
|     props: { | ||||
|         wopiUrl: { | ||||
|             type: String, | ||||
|             required: true, | ||||
|         }, | ||||
|         type: { | ||||
|             type: String, | ||||
|             required: true, | ||||
|         }, | ||||
|         options: { | ||||
|             type: Object, | ||||
|             required: false, | ||||
|         }, | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             modal: { | ||||
|                 showModal: false, | ||||
|                 modalDialogClass: "modal-fullscreen", //modal-dialog-scrollable | ||||
|             }, | ||||
|             logo: logo, | ||||
|             loading: false, | ||||
|             mime: [ | ||||
|                 // TODO temporary hardcoded. to be replaced by twig extension or a collabora server query | ||||
|                 "application/clarisworks", | ||||
|                 "application/coreldraw", | ||||
|                 "application/macwriteii", | ||||
|                 "application/msword", | ||||
|                 "application/pdf", | ||||
|                 "application/vnd.lotus-1-2-3", | ||||
|                 "application/vnd.ms-excel", | ||||
|                 "application/vnd.ms-excel.sheet.binary.macroEnabled.12", | ||||
|                 "application/vnd.ms-excel.sheet.macroEnabled.12", | ||||
|                 "application/vnd.ms-excel.template.macroEnabled.12", | ||||
|                 "application/vnd.ms-powerpoint", | ||||
|                 "application/vnd.ms-powerpoint.presentation.macroEnabled.12", | ||||
|                 "application/vnd.ms-powerpoint.template.macroEnabled.12", | ||||
|                 "application/vnd.ms-visio.drawing", | ||||
|                 "application/vnd.ms-word.document.macroEnabled.12", | ||||
|                 "application/vnd.ms-word.template.macroEnabled.12", | ||||
|                 "application/vnd.ms-works", | ||||
|                 "application/vnd.oasis.opendocument.chart", | ||||
|                 "application/vnd.oasis.opendocument.formula", | ||||
|                 "application/vnd.oasis.opendocument.graphics", | ||||
|                 "application/vnd.oasis.opendocument.graphics-flat-xml", | ||||
|                 "application/vnd.oasis.opendocument.graphics-template", | ||||
|                 "application/vnd.oasis.opendocument.presentation", | ||||
|                 "application/vnd.oasis.opendocument.presentation-flat-xml", | ||||
|                 "application/vnd.oasis.opendocument.presentation-template", | ||||
|                 "application/vnd.oasis.opendocument.spreadsheet", | ||||
|                 "application/vnd.oasis.opendocument.spreadsheet-flat-xml", | ||||
|                 "application/vnd.oasis.opendocument.spreadsheet-template", | ||||
|                 "application/vnd.oasis.opendocument.text", | ||||
|                 "application/vnd.oasis.opendocument.text-flat-xml", | ||||
|                 "application/vnd.oasis.opendocument.text-master", | ||||
|                 "application/vnd.oasis.opendocument.text-master-template", | ||||
|                 "application/vnd.oasis.opendocument.text-template", | ||||
|                 "application/vnd.oasis.opendocument.text-web", | ||||
|                 "application/vnd.openxmlformats-officedocument.presentationml.presentation", | ||||
|                 "application/vnd.openxmlformats-officedocument.presentationml.slideshow", | ||||
|                 "application/vnd.openxmlformats-officedocument.presentationml.template", | ||||
|                 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | ||||
|                 "application/vnd.openxmlformats-officedocument.spreadsheetml.template", | ||||
|                 "application/vnd.openxmlformats-officedocument.wordprocessingml.document", | ||||
|                 "application/vnd.openxmlformats-officedocument.wordprocessingml.template", | ||||
|                 "application/vnd.sun.xml.calc", | ||||
|                 "application/vnd.sun.xml.calc.template", | ||||
|                 "application/vnd.sun.xml.chart", | ||||
|                 "application/vnd.sun.xml.draw", | ||||
|                 "application/vnd.sun.xml.draw.template", | ||||
|                 "application/vnd.sun.xml.impress", | ||||
|                 "application/vnd.sun.xml.impress.template", | ||||
|                 "application/vnd.sun.xml.math", | ||||
|                 "application/vnd.sun.xml.writer", | ||||
|                 "application/vnd.sun.xml.writer.global", | ||||
|                 "application/vnd.sun.xml.writer.template", | ||||
|                 "application/vnd.visio", | ||||
|                 "application/vnd.visio2013", | ||||
|                 "application/vnd.wordperfect", | ||||
|                 "application/x-abiword", | ||||
|                 "application/x-aportisdoc", | ||||
|                 "application/x-dbase", | ||||
|                 "application/x-dif-document", | ||||
|                 "application/x-fictionbook+xml", | ||||
|                 "application/x-gnumeric", | ||||
|                 "application/x-hwp", | ||||
|                 "application/x-iwork-keynote-sffkey", | ||||
|                 "application/x-iwork-numbers-sffnumbers", | ||||
|                 "application/x-iwork-pages-sffpages", | ||||
|                 "application/x-mspublisher", | ||||
|                 "application/x-mswrite", | ||||
|                 "application/x-pagemaker", | ||||
|                 "application/x-sony-bbeb", | ||||
|                 "application/x-t602", | ||||
|             ], | ||||
|         }; | ||||
|     }, | ||||
|     computed: { | ||||
|         isOpenDocument() { | ||||
|             if (this.mime.indexOf(this.type) !== -1) { | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
|         noText() { | ||||
|             if (typeof this.options.noText !== "undefined") { | ||||
|                 return this.options.noText === true; | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
|         isChangeIcon() { | ||||
|             if (typeof this.options.changeIcon !== "undefined") { | ||||
|                 return !( | ||||
|                     this.options.changeIcon === null || | ||||
|                     this.options.changeIcon === "" | ||||
|                 ); | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
|         isChangeClass() { | ||||
|             if (typeof this.options.changeClass !== "undefined") { | ||||
|                 return !( | ||||
|                     this.options.changeClass === null || | ||||
|                     this.options.changeClass === "" | ||||
|                 ); | ||||
|             } | ||||
|             return false; | ||||
|         }, | ||||
|     }, | ||||
|     methods: { | ||||
|         openModal() { | ||||
|             this.loading = true; | ||||
|             this.modal.showModal = true; | ||||
|         }, | ||||
|         loaded() { | ||||
|             this.loading = false; | ||||
|         }, | ||||
|     }, | ||||
|     i18n: { | ||||
|         messages: { | ||||
|             fr: { | ||||
|                 online_edit_document: "Éditer en ligne", | ||||
|                 save_and_quit: "Enregistrer et quitter", | ||||
|                 loading: "Chargement de l'éditeur en ligne", | ||||
|                 invalid_title: "Format incompatible", | ||||
|                 invalid_message: | ||||
|                     "Désolé, ce format de document n'est pas éditable en ligne.", | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| div.wopi-frame { | ||||
|     div.modal-header { | ||||
|         border-bottom: 0; | ||||
|         background-color: var(--bs-primary); | ||||
|         color: white; | ||||
|     } | ||||
|     div.modal-body { | ||||
|         padding: 0; | ||||
|         overflow-y: unset !important; | ||||
|         iframe { | ||||
|             height: 100%; | ||||
|             width: 100%; | ||||
|         } | ||||
|         div.loading { | ||||
|             position: absolute; | ||||
|             color: var(--bs-chill-gray); | ||||
|             top: calc(50% - 30px); | ||||
|             left: calc(50% - 30px); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| </style> | ||||
| @@ -54,6 +54,11 @@ const messages = { | ||||
|             residential_address: "Adresse de résidence", | ||||
|             located_at: "réside chez", | ||||
|         }, | ||||
|         comment: { | ||||
|             label: "Commentaire", | ||||
|             editor_simple: "Simple", | ||||
|             editor_rich: "Riche" | ||||
|         } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -136,6 +136,59 @@ | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <h2>Fix the title in the flex table</h2> | ||||
|  | ||||
| <p>This will fix the layout of the row, with a "title" element, and an aside element. Using <code>css grid</code>, this is quite safe and won't overflow</p> | ||||
|  | ||||
| <xmp> | ||||
|     <div class="flex-table"> | ||||
|         <div class="item-bloc"> | ||||
|             <div class="item-row"> | ||||
|                 <div class="item-two-col-grid"> | ||||
|                     <div class="title">This is my title</div> | ||||
|                     <div class="aside">Aside value</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="item-bloc"> | ||||
|             <div class="item-row"> | ||||
|                 <div class="item-two-col-grid"> | ||||
|                     <div class="title"> | ||||
|                         <div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div> | ||||
|                         <div>This is a second line</div> | ||||
|                     </div> | ||||
|                     <div class="aside">Aside value</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </xmp> | ||||
|  | ||||
|     <p>will render:</p> | ||||
|  | ||||
|     <div class="flex-table"> | ||||
|         <div class="item-bloc"> | ||||
|             <div class="item-row"> | ||||
|                 <div class="item-two-col-grid"> | ||||
|                     <div class="title">This is my title</div> | ||||
|                     <div class="aside">Aside value</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="item-bloc"> | ||||
|             <div class="item-row"> | ||||
|                 <div class="item-two-col-grid"> | ||||
|                     <div class="title"> | ||||
|                         <div><h3>This is my title, which can be very long and take a lot of place. But it is wrapped successfully, and won't disturb the placement of the aside block</h3></div> | ||||
|                         <div>This is a second line</div> | ||||
|                     </div> | ||||
|                     <div class="aside">Aside value</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
| <h2>Wrap-list</h2> | ||||
| <p>Une liste inline qui s'aligne, puis glisse sous son titre.</p> | ||||
| <div class="wrap-list debug"> | ||||
| @@ -392,4 +445,12 @@ Toutes les classes btn-* de bootstrap sont fonctionnelles | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| <div class="row"> | ||||
|     <h1>Badges</h1> | ||||
|  | ||||
|     <span class="badge-accompanying-work-type-simple">Action d'accompagnement</span> | ||||
|     <span class="badge-activity-type-simple">Type d'échange</span> | ||||
|     <span class="badge-calendar-simple">Rendez-vous</span> | ||||
| </div> | ||||
|  | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -214,7 +214,9 @@ | ||||
|  | ||||
| {% block private_comment_widget %} | ||||
|     {% for entry in form %} | ||||
|         {{ form_widget(entry) }} | ||||
|         <div id="comment-app-{{ form.vars.id }}" data-field-name="{{ form.vars.full_name }}"> | ||||
|             {{ form_widget(entry, { attr: { ckeditor: 'true' } }) }} | ||||
|         </div> | ||||
|     {% endfor %} | ||||
| {% endblock %} | ||||
|  | ||||
| @@ -224,7 +226,9 @@ | ||||
|  | ||||
| {% block comment_widget %} | ||||
|     {% for entry in form %} | ||||
|         {{ form_widget(entry) }} | ||||
|         <div id="comment-app-{{ form.vars.id }}" data-field-name="{{ form.vars.full_name }}"> | ||||
|             {{ form_widget(entry, { attr: { ckeditor: 'true' } }) }} | ||||
|         </div> | ||||
|     {% endfor %} | ||||
| {% endblock comment_widget %} | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
|     {{ encore_entry_script_tags('mod_pickentity_type') }} | ||||
|     {{ encore_entry_script_tags('mod_entity_workflow_subscribe') }} | ||||
|     {{ encore_entry_script_tags('page_workflow_show') }} | ||||
|     {{ encore_entry_script_tags('mod_wopi_link') }} | ||||
|     {{ encore_entry_script_tags('mod_document_action_buttons_group') }} | ||||
|     {{ encore_entry_script_tags('mod_workflow_attachment') }} | ||||
| {% endblock %} | ||||
| @@ -19,7 +18,6 @@ | ||||
|     {{ encore_entry_link_tags('mod_pickentity_type') }} | ||||
|     {{ encore_entry_link_tags('mod_entity_workflow_subscribe') }} | ||||
|     {{ encore_entry_link_tags('page_workflow_show') }} | ||||
|     {{ encore_entry_link_tags('mod_wopi_link') }} | ||||
|     {{ encore_entry_link_tags('mod_document_action_buttons_group') }} | ||||
|     {{ encore_entry_link_tags('mod_workflow_attachment') }} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -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', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user