diff --git a/.changes/unreleased/Feature-20240530-160003.yaml b/.changes/unreleased/Feature-20240530-160003.yaml new file mode 100644 index 000000000..6b6baedc5 --- /dev/null +++ b/.changes/unreleased/Feature-20240530-160003.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: | + Upgrade import of address list to the last version of compiled addresses of belgian-best-address +time: 2024-05-30T16:00:03.440767606+02:00 +custom: + Issue: "" diff --git a/.changes/unreleased/Feature-20240531-190242.yaml b/.changes/unreleased/Feature-20240531-190242.yaml new file mode 100644 index 000000000..083298a26 --- /dev/null +++ b/.changes/unreleased/Feature-20240531-190242.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: | + Upgrade CKEditor and refactor configuration with use of typescript +time: 2024-05-31T19:02:42.776662753+02:00 +custom: + Issue: "" diff --git a/.changes/unreleased/Feature-20250808-120802.yaml b/.changes/unreleased/Feature-20250808-120802.yaml new file mode 100644 index 000000000..50d1eb8ba --- /dev/null +++ b/.changes/unreleased/Feature-20250808-120802.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Create invitation list in user menu +time: 2025-08-08T12:08:02.446361367+02:00 +custom: + Issue: "385" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20251007-155945.yaml b/.changes/unreleased/Feature-20251007-155945.yaml new file mode 100644 index 000000000..9b59e7ea5 --- /dev/null +++ b/.changes/unreleased/Feature-20251007-155945.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Admin interface for Motive entity +time: 2025-10-07T15:59:45.597029709+02:00 +custom: + Issue: "" + SchemaChange: No schema change diff --git a/.changes/unreleased/Feature-20251022-111552.yaml b/.changes/unreleased/Feature-20251022-111552.yaml new file mode 100644 index 000000000..058e40d82 --- /dev/null +++ b/.changes/unreleased/Feature-20251022-111552.yaml @@ -0,0 +1,6 @@ +kind: Feature +body: Add an admin interface for Motive entity +time: 2025-10-22T11:15:52.13937955+02:00 +custom: + Issue: "" + SchemaChange: Add columns or tables diff --git a/.changes/unreleased/Fixed-20251106-161605.yaml b/.changes/unreleased/Fixed-20251106-161605.yaml new file mode 100644 index 000000000..3962daf8a --- /dev/null +++ b/.changes/unreleased/Fixed-20251106-161605.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Fix suggestion of referrer when creating notification for accompanyingPeriodWorkDocument +time: 2025-11-06T16:16:05.861813041+01:00 +custom: + Issue: "428" + SchemaChange: No schema change diff --git a/.changes/unreleased/Major-20260326-162018.yaml b/.changes/unreleased/Major-20260326-162018.yaml new file mode 100644 index 000000000..3d52ab86c --- /dev/null +++ b/.changes/unreleased/Major-20260326-162018.yaml @@ -0,0 +1,6 @@ +kind: Major +body: Add a bundle to deal with tickets +time: 2026-03-26T16:20:18.302331043+01:00 +custom: + Issue: "" + SchemaChange: Add columns or tables diff --git a/.changie.yaml b/.changie.yaml index 0b441f118..cbd628f06 100644 --- a/.changie.yaml +++ b/.changie.yaml @@ -60,6 +60,8 @@ kinds: auto: patch - label: UX auto: patch + - label: Major + auto: major newlines: afterChangelogHeader: 1 beforeChangelogVersion: 1 diff --git a/.editorconfig b/.editorconfig index bede621e3..49ea12528 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,11 +19,11 @@ max_line_length = 80 [COMMIT_EDITMSG] max_line_length = 0 -[*.{js, vue, ts}] +[*.{js,vue,ts}] indent_size = 2 indent_style = space -[.rst] -ident_size = 3 -ident_style = space +[*.rst] +indent_size = 3 +indent_style = space diff --git a/.eslint-baseline.json b/.eslint-baseline.json index a8bf01c0d..21c38125a 100644 --- a/.eslint-baseline.json +++ b/.eslint-baseline.json @@ -17,19 +17,19 @@ }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue", - "line": 19, - "column": 30, + "line": 10, + "column": 22, "ruleId": "vue/valid-v-else", "message": "'v-else' directives require no attribute value.", - "hash": "505f99b24500a684eec3c6bf870426fcd841c20b" + "hash": "c4d34a0df38baaf72092179b2e066c91b6f6733a" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/Components/CalendarActive.vue", - "line": 54, + "line": 45, "column": 10, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'mapGetters' is defined but never used.", - "hash": "01e3928f7d9be0accb6db3894b158dd683a685ff" + "hash": "8e80aff377fdd5af9686df6a89a4ba650911f02a" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/index.js", @@ -82,50 +82,50 @@ { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", "line": 4, - "column": 5, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'DateTime' is defined but never used.", - "hash": "fdf38ad15813c4a931b93f4a85db985ca71105fc" + "hash": "88be21db222be347a0cf7f8fbfaba531d5cb0418" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", "line": 14, - "column": 27, + "column": 25, "ruleId": "@typescript-eslint/no-empty-object-type", "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", - "hash": "9602ec11eda8f46ff5712d69dcd87419babcd6f0" + "hash": "6c1437cbb501093bde9c9f7eeda4e4853e558c1a" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", "line": 16, - "column": 20, + "column": 18, "ruleId": "@typescript-eslint/no-empty-object-type", "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", - "hash": "23f7a8a1c3cba955077ddbd604bad6805ce30ad2" + "hash": "f03d8114bfe562d84f8160f5d122562e484b5de1" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Calendar/store/utils.ts", "line": 18, - "column": 19, + "column": 17, "ruleId": "@typescript-eslint/no-empty-object-type", "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", - "hash": "abbc89e186c74d0f71f45cfda31c490339e39078" + "hash": "de706baa154ead33e72ce520c50cdd16a85d1265" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/Invite/Answer.vue", - "line": 95, - "column": 13, + "line": 86, + "column": 19, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'payload' is defined but never used.", - "hash": "66c545917093ba30f1d6ca10ddaa676140e749bd" + "hash": "8c189da3c258c22dfdd1e0a72d0f0ef5464c26eb" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", "line": 77, - "column": 16, + "column": 12, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "d29fb2fc9299c48082167b2fa4c427c569716bd6" + "hash": "8040b05fb0422e91d049dd0d7fb4604b4455bf5e" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue", @@ -154,50 +154,50 @@ { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/index2.ts", "line": 12, - "column": 11, + "column": 9, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'app' is assigned a value but never used.", - "hash": "6422c0876b992a3c5174faa99ef06d8339b74b4e" + "hash": "3ebdc6637bc62a230176a4dff37a5900061e6eed" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/index.ts", - "line": 46, - "column": 20, + "line": 44, + "column": 74, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "5bf7ba0b0200fb6788d50df669e6b7701f8a87bb" + "hash": "7ac3276b20b1029e510bcbbbc8572fc34400e204" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", "line": 57, - "column": 32, + "column": 20, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "a733a5edfe4c434ed63682bcbd5fe65f175a1b85" + "hash": "d25bbc0a04099c361d99f34b0c731892c7b008a7" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", "line": 64, - "column": 32, + "column": 20, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "d83b1879b5fc6179c045ad1981421f46a7cbbd56" + "hash": "7c53ad6034cebccfe119a1ee7dd011984a720676" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", "line": 71, - "column": 32, + "column": 20, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "222eed84495212735a60fa05e0c7f298c7bc33e1" + "hash": "110e12e976ae0f8fe15cd70b974d99ee381bb6fb" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/fullcalendar.ts", "line": 72, - "column": 26, + "column": 18, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'_' is defined but never used.", - "hash": "0cf85eccce08b65a8590f7318cace58cbb21ea08" + "hash": "7919b6b2becd817e0fad8c51602b0f8c2fc0f5f2" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/store/modules/me.ts", @@ -210,90 +210,90 @@ { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", "line": 108, - "column": 47, + "column": 35, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reject' is defined but never used.", - "hash": "1a771f9f65375cac2002ed7d706184355685d378" + "hash": "e57ca97296d09e44a8e19573138a4f39f7777778" + }, + { + "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", + "line": 145, + "column": 15, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"users\" prop.", + "hash": "26378799855b04bc15aa3e030b9ede95f3139c4d" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", "line": 148, - "column": 29, + "column": 15, "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"users\" prop.", - "hash": "d9f7e517892b6588be17516fd077c3149ccbefd9" + "message": "Unexpected mutation of \"calendarEvents\" prop.", + "hash": "279aee22cdc99740903c9fee736897a65074a8bb" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", "line": 151, - "column": 29, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"calendarEvents\" prop.", - "hash": "47c270d8c0bcda67ea04a83ba267eca3e70fa7fb" - }, - { - "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 154, - "column": 59, + "column": 41, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reject' is defined but never used.", - "hash": "7cf30f0d2dc567ea0dfb44dadfc9ecf4977e7b95" + "hash": "2c597422f93f4b397bfe96365007c602d349ad0c" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 155, - "column": 41, + "line": 152, + "column": 21, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"users\" prop.", - "hash": "7876348a3eee16be7effd3e992a6aaa2334cfd24" + "hash": "75af3ee3d935ce4d62c037461e552cef3ab0f291" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 164, - "column": 63, + "line": 158, + "column": 47, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reject' is defined but never used.", - "hash": "0f308d7417bfc294b28eea6890666a2d91e31ecd" + "hash": "6ffe789bbd1d731daaf5c2bff4582372fd342dc1" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 185, - "column": 57, + "line": 170, + "column": 27, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"calendarEvents\" prop.", - "hash": "5a021a581a33d3aeea21c65e023902f2123bb562" + "hash": "ef1795af6c50719c19ec3bffd8e511d06a9bfac2" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 218, - "column": 17, + "line": 197, + "column": 9, "ruleId": "@typescript-eslint/prefer-for-of", "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", - "hash": "655811d4dad8de23b3d37dfbf02f7fca18b6d8f2" + "hash": "bc4a3fd50f5afd095d8e938885291d948c4ba689" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 235, - "column": 13, + "line": 214, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"calendarEvents\" prop.", - "hash": "19317d9a564b8b10355cac1eeb9308c7d49c0dd1" + "hash": "6a27b96c77fc2747b4fd0f82ccfedc44138b0aa5" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 240, - "column": 13, + "line": 219, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"users\" prop.", - "hash": "95e478eca53a2acfa8edd0aa5747e73b0da01d0c" + "hash": "61c0ca5de675766483584b7363b048356f9c55f8" }, { "path": "src/Bundle/ChillCalendarBundle/Resources/public/vuejs/_components/CalendarUserSelector/CalendarUserSelector.vue", - "line": 246, - "column": 13, + "line": 225, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"users\" prop.", - "hash": "0b66e38f0c0bbe17aa7c548dc127be033133ce06" + "hash": "d87ce5e7fbcefc884fb7be907d3c3fef53921ab4" }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts", @@ -489,11 +489,11 @@ }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue", - "line": 80, - "column": 5, + "line": 78, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'StoredObjectVersion' is defined but never used.", - "hash": "2fadbe9e331a66f44b54782acc1420c4b487e1f6" + "hash": "da2ec95bcd2bb80819ef2032e40b0d7237acba31" }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts", @@ -522,10 +522,10 @@ { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", "line": 11, - "column": 5, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'download_and_decrypt_doc' is defined but never used.", - "hash": "9a803f1fe608ec60ab065aa8f76e50e2ef1fa4c2" + "hash": "ddc9ed7c384b9da6ed1398551cefe7d244cb0c9e" }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", @@ -537,11 +537,11 @@ }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue", - "line": 50, - "column": 11, + "line": 48, + "column": 9, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reset_pending' is assigned a value but never used.", - "hash": "96fde3634a150f1253fac2f2f2bdcfe6e0daf82a" + "hash": "9919afcb6b2724f5b6be69caa1b487026b9260fe" }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue", @@ -561,19 +561,19 @@ }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue", - "line": 15, + "line": 13, "column": 8, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'WopiEditButton' is defined but never used.", - "hash": "80cb06a18ac4db47e8d120103704ec6988696b79" + "hash": "f197263cc1b8a5eeadd87e2e2c44eccc618375ec" }, { "path": "src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts", "line": 3, - "column": 5, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'StoredObjectStatus' is defined but never used.", - "hash": "e868cee022cd1499bb54f7b5503a8a822773e097" + "hash": "ceb106361b5267900ff8852f6ff191698e065e13" }, { "path": "src/Bundle/ChillJobBundle/src/Resources/public/module/cv_edit/index.js", @@ -696,28 +696,36 @@ "hash": "a78b1ecd6fe7ca192d7f00aee8b270d435a9042a" }, { - "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", - "line": 73, - "column": 5, - "ruleId": "prefer-const", - "message": "'cal' is never reassigned. Use 'const' instead.", - "hash": "53586ea7ec719f07750a8874db79f5e76583d5e0" + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 122, + "column": 13, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "1025f4dd46e3d95f1bfc312c2613f27c76e3cdaa" }, { - "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", - "line": 79, - "column": 5, - "ruleId": "prefer-const", - "message": "'time' is never reassigned. Use 'const' instead.", - "hash": "19821279c0b2d7e69418db6877c317f8c6e2dacc" + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 132, + "column": 18, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "21b7b1799a5ef003b236ede206bd28038d465187" }, { - "path": "src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts", - "line": 85, - "column": 5, - "ruleId": "prefer-const", - "message": "'offset' is never reassigned. Use 'const' instead.", - "hash": "244b65de0ab0791ec89219058c5cb4f2e11622c7" + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 138, + "column": 11, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "323a777776fec63aba9e641daaf9109993ad5f2f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts", + "line": 356, + "column": 15, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "c0e5eea3e4805d847ac1a5024e2232152a0f6340" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/lib/download-report/download-report.js", @@ -850,26 +858,26 @@ { "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", "line": 141, - "column": 5, + "column": 3, "ruleId": "@typescript-eslint/prefer-for-of", "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", - "hash": "cd5fc994be294fde5fef27ba841c9af454b81dc0" + "hash": "c280bbe030fe9a793f531ce00c1a4a6c6fdcfdc0" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", "line": 153, - "column": 5, + "column": 3, "ruleId": "@typescript-eslint/prefer-for-of", "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", - "hash": "381830dd078845aeab5ea366757541cd7c77ca3f" + "hash": "98e7c2f525eddcfd8d0f839c9bf69b7c56c9d537" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts", "line": 162, - "column": 9, + "column": 5, "ruleId": "@typescript-eslint/prefer-for-of", "message": "Expected a `for-of` loop instead of a `for` loop with this simple iteration.", - "hash": "768b1976fb935d4a4b0890f00be2299b7ff60170" + "hash": "66a4dba7d1d666cbac1ed22eb2a7916cf97ca5ec" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/module/disable-buttons/index.js", @@ -919,14 +927,6 @@ "message": "'_e' is defined but never used.", "hash": "1d6448401778e8c56554020fe5abd47851ed33f3" }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js", - "line": 9, - "column": 7, - "ruleId": "@typescript-eslint/no-unused-vars", - "message": "'app' is assigned a value but never used.", - "hash": "2b4f1b7b1ec87616e72a89871f91845c16200435" - }, { "path": "src/Bundle/ChillMainBundle/Resources/public/page/location/index.js", "line": 63, @@ -994,418 +994,418 @@ { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue", "line": 79, - "column": 55, + "column": 39, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reject' is defined but never used.", - "hash": "29ea9ef3f1b2cb970e8103383bcde12a4ab66c25" + "hash": "3014fecdefbab379cc54e6abe7532c892cc788f6" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue", "line": 101, - "column": 55, + "column": 39, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'reject' is defined but never used.", - "hash": "b81ee6947049c6876f81410cfbb499dc43da4374" + "hash": "03336dded33c9831e1f8c4b950591b0a24eac678" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", - "line": 516, - "column": 21, + "line": 506, + "column": 11, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"context\" prop.", - "hash": "984c4203f2ac1e1bb65f9ce76ecd03b763cfaa83" + "hash": "ac876f2e55239ccbfb9752622a9b11f72cee7fe0" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue", - "line": 517, - "column": 21, + "line": 507, + "column": 11, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"context\" prop.", - "hash": "c9fb019bc21bfa77d989ed596913b99dd653c594" + "hash": "ff9ee3e9b5478747ffe41f3df282731fb40697b9" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 125, - "column": 17, + "line": 121, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "792310bc5def2c7b45f50da97cd8818d82862e8a" + "hash": "af8ef45aa80416b447031e4ca030b88f5bfc882b" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 133, - "column": 17, + "line": 129, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "a69e5335393f67a83d89720af34a4385a7d7e665" + "hash": "b7452f0d725acbb827e558adcf8a410fe505a7b0" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 141, - "column": 17, + "line": 137, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "2d5a5e680ff207ad97c7e7b7d999064b561dfd8a" + "hash": "91ad00b0f5a6604ca19e866add92814bf244cfed" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 145, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "806de3082d5ecc19585685b660f162a76eb7f670" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 153, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "a99ccff1b281194feae8d753a246104f0c0eb9ad" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 161, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "31656122b25dfb1bfcd971f31277f38ccd438236" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", + "line": 169, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "deabf81e1a609a1a25118da3752b2a752a5a08ef" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 126, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "64cc51b8916fa6c01ec548dc8752f174d5596a6a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 134, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "207f09f4cd86ce7ca0bdd3d5f291425fe2999657" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", + "line": 148, + "column": 7, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "a59e0eaf70fe261e3149bcbb7132abaa63415666" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", "line": 149, - "column": 17, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "e4c1ecd7ae77d46ac3625c5bbe92a24d6a964db9" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 157, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "4dece2db87c6ce1c04ae06c088ddfe916c1c0c61" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 165, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "facc7a0f17bdf19396fae3d0de3da82e60503c0d" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue", - "line": 173, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "19de32c76518387218264d7c4dab914d143a9cca" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 130, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "239ac02a02694d5b20ab30d4c7ce5838c51d1515" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 138, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "a54f9bc6d1edfa4df93c7dd7d409cfef3fccf99e" + "hash": "f345ef25597c77e5dd4317a02ab2791c90aa25e2" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", "line": 152, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "74a5f664d18f3916ea908897fcd0291cb0128f29" + "hash": "00f3a0b5fa27de5d9e734198d41ca1ad618ef540" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", "line": 153, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "740ea5d793c7a34c9f352d8b333f3aa04cc80ee8" + "hash": "a2acfe6631535d3f4bc4ca24cc3ece7f0ee60aad" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 156, - "column": 13, + "line": 154, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "af8aca18f0226a5988ed90d44d95e2d607bfb5e6" + "hash": "e0126147227c6b442c091a53a890f284fb3606e4" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 157, - "column": 13, + "line": 159, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "7bc2453017793ae20cd6c10005f941d384b59d84" + "hash": "8175f01db63eff68466f07f76f6b3eaa2e3228a6" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 158, - "column": 13, + "line": 174, + "column": 19, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "571b4ee5f22358dd165ec59696bb3439b7c9ff6c" + "hash": "42b7030eadb5b2dc67c68e5548a38f9ac8d13b86" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 163, - "column": 13, + "line": 191, + "column": 21, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "cfcb5946c86e289fc61623a794284a5a272d02e8" + "hash": "2df60edf992ad26ee5a2eac8fe561d71368dc15a" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 178, - "column": 37, + "line": 208, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "0ec402e43cb08bf129e0737c0d2c4f6d0c7af8bd" + "hash": "5f4fac614979fb7336c9c4f19480b8b8708fda3d" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 196, - "column": 41, + "line": 209, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "ec178d33e067aac892e015002afb6f3a2ff98762" + "hash": "7820e56bdd109fe60f3420928daab354fb9cff74" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 214, - "column": 17, + "line": 210, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "c0f4e5454e672b6064eb9cf6c235c6810f7bfa80" + "hash": "c8bab06e7f5c05ddccee1fd5fe1da8e35bdc1544" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 215, - "column": 17, + "line": 240, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "e3dd840d2474f9865a45822872bf9ecfb15961d7" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 216, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "a32a60382b145cc7a4a7ebe01ec435b8e3103320" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue", - "line": 246, - "column": 13, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "082447e5c731012f3acc282943502775dfd24797" + "hash": "b35122fae4377d9a380bb1cb420420b432413bac" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 118, - "column": 20, + "column": 14, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "d4fba4fe09af3c0937c0dd164928c8930c1591b5" + "hash": "c92225e1cc64c1b212c9737caf72c0529ce91961" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 118, - "column": 20, + "column": 14, "ruleId": "vue/no-side-effects-in-computed-properties", "message": "Unexpected side effect in \"cities\" computed property.", - "hash": "1113a114d5aaf9f32f442916d25458541c5af35c" + "hash": "702ab2d1d2ab13efd4ca7e2ee8dd90d82438d4ee" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 124, - "column": 17, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "fa56a7c93583f0a9d0c2ecac10228c4f4fc1bc3a" + "hash": "e05c614b75ec62d0f2ee13dd575ed5211dbf7e26" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 132, - "column": 17, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "9fe87937ea67d1dae95fb3d44d4be0da2eba0905" + "hash": "4fe1057d1cde0d635e89fcf43473a1a2001e21a0" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 146, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "eaaaaee5fb2e324ffe0a68eefe340dabdf162324" + "hash": "8e6ce37ada2dd82cda2275a9fb3e90c19480a8dd" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 147, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "2de47b4a4ddbe546b3fce9898af48b72853364bf" + "hash": "fd8edbb6c48ff7dc090ddc3974f3aefc78bae6c2" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 148, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "ab4f478fbfbc954b8dff75176dcd432f9ff28cfc" + "hash": "897ef151fcc7061d7bc6831bb967f1a973af43ca" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 153, - "column": 21, + "column": 11, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "1d907d149f9ddb62e32140a90efe9a74b3e71fef" + "hash": "2d0abac14f725188683878955a6b3ff45fffeecc" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 165, + "column": 7, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "a3533e6ef4354b4d1003e6064661fc5ca3d59728" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 166, + "column": 7, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "63bd51088f7cd5b98ca0a9b471110f679f83fcce" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 167, - "column": 13, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "8aa37d2d4f011773e68838a2c88017875de563b5" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 168, - "column": 13, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "a4827a357e52a51fa9262319114d81a130296acf" + "hash": "0246f52bb6bf530f49d02770e3873a27ebb90892" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 169, - "column": 13, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "a4c9715664202949e3242b8d4aa4098288b46dc4" + "hash": "295f6d4268e7aa8073b9f6faea7703585e165626" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", "line": 171, + "column": 7, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "0d46e4ab9ef4bf684de06fd12d96bc40c92d804a" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 180, + "column": 7, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "fbfb35070f8dc5d818591616f058b6d854afd56f" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", + "line": 190, "column": 17, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "f3e9e21e433e90ec7b615b8940d43c4177372b66" + "hash": "1489af475c20ec74d635d70f0241b86954e7a5d8" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 174, - "column": 13, + "line": 209, + "column": 19, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "770b7a24cc24b380e88db47d62422c8e1ece2571" + "hash": "bf1979593106c267ac3421aa94fc23527495217c" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 183, - "column": 13, + "line": 227, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "2aef3c519a9ec6abcfe7573989d3de19d5c4c752" + "hash": "37193e85213d03580d73113b5238a68b146deef8" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 193, - "column": 33, + "line": 228, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "5d1f97e4d7d9f47399d312e8b9f95ef9e3843b8c" + "hash": "36ff0e9cce561e0c2b96170b5bb8a75b69e6cb09" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 213, - "column": 37, + "line": 229, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "c1df874f790ef0c036bf58ae8a8db1ee173685d4" + "hash": "25aca14c252233613e7fa9353c72b33544078bfd" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 232, - "column": 17, + "line": 230, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "476e6588a28ac9382e8b9d2e63a8babecd23bad8" + "hash": "ca4f5e8dd145f1de860c9e9fd0c78da55224f049" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 233, - "column": 17, + "line": 262, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "6a0c82ba72d6d87217bf33a6ad8e40a4b81bc802" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 234, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "741d5af6c7d90041c0dc1c1df2e8699b80fca69a" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 235, - "column": 17, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "c3ffd141f58d532663875cc5c7d338ed00db2a6d" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue", - "line": 267, - "column": 13, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "2700f258396516a2fe971618fafbcdf72cdda3ab" + "hash": "299bda11882cd026de23f23fd2afd89051dd50be" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", - "line": 94, - "column": 13, + "line": 90, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "4be1b0592efa775092a91a1d744e16ce98bd216e" + "hash": "5720a11e08bafbf9983f082de3ef255bafd72b3a" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue", - "line": 99, - "column": 13, - "ruleId": "vue/no-mutating-props", - "message": "Unexpected mutation of \"entity\" prop.", - "hash": "19b54b6d76c30249d520a296f826eda9d6eb0668" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", "line": 95, - "column": 17, + "column": 7, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "84e13d1fdc79f4568634a78df281adbe81739cbd" + "hash": "ccf539fa491a02cd526ee6cf4517ecfe1e9d5a58" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", - "line": 103, - "column": 17, + "line": 92, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "1eed832462e52537402a2825655733f0f2d391d9" + "hash": "2058a778c66e3d4005cdfb4354ede1d93e8e1ab6" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue", + "line": 100, + "column": 9, + "ruleId": "vue/no-mutating-props", + "message": "Unexpected mutation of \"entity\" prop.", + "hash": "20b24f03b3260f915df456ce4d54775f2dda68b6" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", - "line": 169, - "column": 17, + "line": 166, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "dcb7b34098062760ddbb849655a5bb3ca65c36d3" + "hash": "2abd05787da25fbdc0c640d88e2d99ad3f1456bf" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue", - "line": 178, - "column": 17, + "line": 175, + "column": 9, "ruleId": "vue/no-mutating-props", "message": "Unexpected mutation of \"entity\" prop.", - "hash": "86b3ecf201025cac36878c5e4bf8850fb9d58cb5" + "hash": "b0662afccb5ca34135a38208fc6a277bb3fbc933" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js", @@ -1432,20 +1432,12 @@ "hash": "52a2051aee948783e053f102a066d4c51155cc54" }, { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue", - "line": 142, - "column": 57, + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue", + "line": 184, + "column": 50, "ruleId": "@typescript-eslint/no-explicit-any", "message": "Unexpected any. Specify a different type.", - "hash": "a68eeba7b2e1e603d83da0946c94cd221134aa99" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue", - "line": 204, - "column": 22, - "ruleId": "vue/return-in-computed-property", - "message": "Expected to return a value in \"buttonMessage\" computed property.", - "hash": "b101c861dc11bc7024857fa2977118cb9f99c02c" + "hash": "a7c54244bbbf7e84bc93b6e34eae406010f99f0c" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/index.js", @@ -1455,69 +1447,101 @@ "message": "'app' is assigned a value but never used.", "hash": "9e6125f4fc387dc362c69cc6e3ce360eb2851f1b" }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/PickEntity/PickEntity.vue", + "line": 105, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'addNewEntities' is defined but never used.", + "hash": "baad83aca50fafbf369b9d2ca0f115713afab7a5" + }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue", - "line": 24, + "line": 21, "column": 13, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'LatLngExpression' is defined but never used.", - "hash": "78f5a83dddf05b38aa9472ab93871e976719ef30" - }, - { - "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue", - "line": 6, - "column": 7, - "ruleId": "@typescript-eslint/no-unused-vars", - "message": "'props' is assigned a value but never used.", - "hash": "29fe0a5d52e46c479aa2e7bb23fb55c53df7b22e" + "hash": "96b45c0371542f6e2a5b6f1b2d4f598698faff68" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", - "line": 114, - "column": 34, + "line": 107, + "column": 32, "ruleId": "vue/require-valid-default-prop", "message": "Type of the default value for 'goToGenerateWorkflowPayload' prop must be a function.", - "hash": "d686fa87cfdc801aaaa08b24e827e503e81e86be" + "hash": "d86b61c318c09e12544ded19f252f6e281e8f985" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", - "line": 170, + "line": 163, "column": 7, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'goToDuplicateRelatedEntity' is assigned a value but never used.", - "hash": "224ddf3abcff96e3e20a0facc7493883958d5a80" + "hash": "a6b7b632e663f282e0f4951d95a6987fa70f4046" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", - "line": 171, - "column": 5, + "line": 164, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'event' is defined but never used.", - "hash": "aa87fd5511528b5a45713fe1eaeda9ae0a8c0975" + "hash": "dd3f0de245d7a2107ad5526965d1a0c29df0ef26" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", - "line": 172, - "column": 5, + "line": 165, + "column": 3, "ruleId": "@typescript-eslint/no-unused-vars", "message": "'workflowName' is defined but never used.", - "hash": "e34bbcf245552e9329efdf7bd64ea3a56f0d4538" + "hash": "d12891a4cc2df02d4b15f30b474edf8b01fd9766" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/PickWorkflow.vue", - "line": 173, + "line": 166, "column": 12, "ruleId": "@typescript-eslint/no-empty-function", "message": "Unexpected empty arrow function.", - "hash": "8bdff7a5b3a7ac1506966a6066a1deb556d30efe" + "hash": "3977a54eb58bc5c558dfb1ce043b14377a746441" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", "line": 3, - "column": 9, + "column": 5, "ruleId": "vue/require-toggle-inside-transition", "message": "The element inside `` is expected to have a `v-if` or `v-show` directive.", - "hash": "0594fb9d0984f4dd1612671aca21b571087ab8ee" + "hash": "f768fd146143b063da0d153ac6591339b7f303d5" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 53, + "column": 18, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "313ac1b6f17e6127cccca65f648fbac8aaeabb28" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 54, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "f13322d4cf3481fb5e434926f1fa550eceb46d0d" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 55, + "column": 18, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "6b4fa4c3d169e4d8128745d001cf5e7b93ef76ce" + }, + { + "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue", + "line": 56, + "column": 23, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "6e1786506d3fe0302b03611def46351cd10abfe1" }, { "path": "src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts", @@ -1543,6 +1567,46 @@ "message": "'tags' is assigned a value but never used.", "hash": "ae9bb2e0651c118ed9efd227e88b86cc83f5d80d" }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/types.ts", + "line": 448, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "b0b2c3b5f1943a4e053cab55c1ba63e93ed42c89" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/types.ts", + "line": 454, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "293570e75ecb111368c38ff158650f2015f2c947" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/types.ts", + "line": 460, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "bbb0c8a2f6c6dc2b30fb90de4ff2989509abac95" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/types.ts", + "line": 466, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "2c1ef23299e3340b47352bfe9ea5e2456838b648" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/types.ts", + "line": 472, + "column": 16, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "cd607050f2ea4ee40eac13633330855257f6d054" + }, { "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js", "line": 19, @@ -1607,14 +1671,6 @@ "message": "'create' is defined but never used.", "hash": "49a7ec9b72de87036e05882d92bea39a7d6595f4" }, - { - "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js", - "line": 17, - "column": 7, - "ruleId": "@typescript-eslint/no-unused-vars", - "message": "'evalFQDN' is assigned a value but never used.", - "hash": "7fc32caafa23addddf44f3acbc5045b4523a0271" - }, { "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/ExportFormActionGoalResult/index.js", "line": 16, @@ -1640,19 +1696,363 @@ "hash": "9e94e6412b8a44e47bfe8e66218cad09cff5bed4" }, { - "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue", - "line": 6, + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_api/OnTheFly.ts", + "line": 10, "column": 8, "ruleId": "@typescript-eslint/no-unused-vars", - "message": "'BadgeEntity' is defined but never used.", - "hash": "951a1b012bdec10c4b859af8b34dd894f63add23" + "message": "'person' is defined but never used.", + "hash": "7b4ac9633123831121214e88cdd60f873b4b0729" }, { - "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUserGroup.vue", - "line": 7, - "column": 8, + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_api/OnTheFly.ts", + "line": 121, + "column": 14, + "ruleId": "@typescript-eslint/no-empty-object-type", + "message": "The `{}` (\"empty object\") type allows any non-nullish value, including literals like `0` and `\"\"`.\n- If that's what you want, disable this lint rule with an inline comment or configure the 'allowObjectTypes' rule option.\n- If you want a type meaning \"any object\", you probably want `object` instead.\n- If you want a type meaning \"any value\", you probably want `unknown` instead.", + "hash": "795f29e8a6541d7dad8a245ae49cc57c50a285ea" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue", + "line": 57, + "column": 25, "ruleId": "@typescript-eslint/no-unused-vars", - "message": "'UserRenderBoxBadge' is defined but never used.", - "hash": "99eba0d8633b2c9497417f4f61ec4194dbb2a96b" + "message": "'nextTick' is defined but never used.", + "hash": "de1d580c9360dc20235dee4e75c70fb40b1e99b4" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue", + "line": 67, + "column": 1, + "ruleId": "vue/no-dupe-keys", + "message": "Duplicate key 'options'. May cause name collision in script or template tag.", + "hash": "bf25f9f8463cac79f15003dd88cf5320ad94f4fd" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonChooseModal.vue", + "line": 110, + "column": 35, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'nextTick' is defined but never used.", + "hash": "30ce64b50e6925476e299015d83a91368c823461" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonChooseModal.vue", + "line": 110, + "column": 45, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'watch' is defined but never used.", + "hash": "798d1f4e8ebf75f80a0ec9b8ecb9d512f6b5d5e8" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonChooseModal.vue", + "line": 181, + "column": 7, + "ruleId": "vue/no-dupe-keys", + "message": "Duplicate key 'suggested'. May cause name collision in script or template tag.", + "hash": "bf7f1c7bb15d493a36af59e81ada2742a08f60c2" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue", + "line": 76, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'isChecked' is assigned a value but never used.", + "hash": "3e27a9a362c3ffeacafe035bdb49c3bdafb8c720" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue", + "line": 45, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'makeFetch' is defined but never used.", + "hash": "b2d0fb3f661bf5939cee122459c0df8b87c1cd81" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue", + "line": 47, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Result' is defined but never used.", + "hash": "ba94838c9c95b943d0c69e57a2c47df7895ad221" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue", + "line": 67, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'toast' is assigned a value but never used.", + "hash": "d7d63d063c097cd4050c25fb9b01263b27a95a11" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeUser.vue", + "line": 13, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'computed' is defined but never used.", + "hash": "ae191df094f7521b0b8f036576de6d0dcf3eb9b6" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/Entity/PersonText.vue", + "line": 20, + "column": 10, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'AltName' is defined but never used.", + "hash": "1b37dfe040579dffff1270d5a9fccc19a5ebf5e0" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 18, + "column": 7, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "e50e95c121a2d829b7fa813c5df2f9898132b4ca" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 53, + "column": 7, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "0ae5f2d5b253de558a8251f64c11d2cebabcaffd" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 74, + "column": 23, + "ruleId": "vue/no-unused-vars", + "message": "'i' is defined but never used.", + "hash": "e63a4f739f7ff8e315fbd1a08f222ae28c29ca62" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 120, + "column": 11, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "038ca805589ad9893bd138483eac424fb681fec0" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 154, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "0ff5adad698abce70fae3318391716304c59bd7d" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 183, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "b905eb46f186f373c2133dfe70de900fb05730c7" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 212, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "436de20322277a35ae9b026d04dfdaf7e411fc1c" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 238, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "2a38d373f47d725155fc84850208600b41ae5dcc" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 265, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "16015ec0e34fdc4738e5b37c6956b8cd89de7b8e" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 292, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "88b1a9b28923c6361c1aaba4835eaa8336d8a055" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 317, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "a7aa83693d3164da77fd65a3f1ac59c542a4ff85" + }, + { + "path": "src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/OnTheFly/PersonEdit.vue", + "line": 466, + "column": 26, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "29a1cb6d8a5017990f389b49a64cbaac7409b87b" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts", + "line": 3, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'Center' is defined but never used.", + "hash": "dbe4fe439b11344264094210bacc85c36d539310" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/types.ts", + "line": 37, + "column": 11, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "650a54fb2d829dd815634fe8bd3b267d14129114" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue", + "line": 132, + "column": 20, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'defineAsyncComponent' is defined but never used.", + "hash": "af21ccf287ad64367d2885f127fb41c00c56101e" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/Entity/ThirdPartyRenderBox.vue", + "line": 168, + "column": 7, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'hasParent' is assigned a value but never used.", + "hash": "2e673452f705cad986b2456d929aa27619a3f2db" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 44, + "column": 21, + "ruleId": "vue/no-dupe-v-else-if", + "message": "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the `v-if` / `v-else-if` chain.", + "hash": "ed6bb276490065781fb93d0bc0a8283577459c4b" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 152, + "column": 13, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "026a2dccb2c30d4bee7ab63d73cf57e950b869b0" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 191, + "column": 9, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "c29f8b40b8b324fa112eeb1470b7cf7f0e555ca4" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 238, + "column": 7, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "82320ece55b2f3962831a309cfcec029fd0e9dde" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 266, + "column": 7, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "518ba2f117d9503657bbe5bdb7c3e0d6052fdfbe" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 294, + "column": 7, + "ruleId": "vue/require-v-for-key", + "message": "Elements in iteration expect to have 'v-bind:key' directives.", + "hash": "3d9a0ad4c7623c553c536c30bc4734a5adfcffb5" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 318, + "column": 46, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'getCurrentInstance' is defined but never used.", + "hash": "f5777ea2ecebf8692e43bf473031eb53f2849367" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 342, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'PERSON_EDIT_ERROR_WHILE_SAVING' is defined but never used.", + "hash": "71f536e898048321814ba01511eba6e4479ab8fe" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 345, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'createPerson' is defined but never used.", + "hash": "82a2499548293567ad1e6ec282f273b4360ae184" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 347, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'WritePersonViolationMap' is defined but never used.", + "hash": "127fafcd0fc7412c14902eb233d70ef0e759d994" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 351, + "column": 3, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'isThirdpartyCompany' is defined but never used.", + "hash": "02f097f6623659789e415f406355ce73d5f6e199" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 357, + "column": 20, + "ruleId": "@typescript-eslint/no-unused-vars", + "message": "'SetCivility' is defined but never used.", + "hash": "aa58bdc1bc120b3814a36476c2222e755cf04e56" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 436, + "column": 27, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "a0cd652c7b020cfbc1e6cc387036feed6924a978" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 451, + "column": 14, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "5c47553fdda9cb471b61287d4799f8c5afc8e461" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 455, + "column": 26, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "e493555f9960aaed8220bb986437e259185d6837" + }, + { + "path": "src/Bundle/ChillThirdPartyBundle/Resources/public/vuejs/_components/OnTheFly/ThirdPartyEdit.vue", + "line": 499, + "column": 30, + "ruleId": "@typescript-eslint/no-explicit-any", + "message": "Unexpected any. Specify a different type.", + "hash": "1286de9566b048e72f98698efe4b3b544d732c98" } ] \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 68a7caaa0..670edfbd4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -99,10 +99,12 @@ build: stage: Composer install image: chill/base-image:8.3-edge variables: + COMPOSER_MEMORY_LIMIT: 3G before_script: - composer config -g cache-dir "$(pwd)/.cache" script: - composer install --optimize-autoloader --no-ansi --no-interaction --no-progress + - php bin/console cache:clear cache: paths: - .cache/ @@ -110,6 +112,7 @@ build: expire_in: 1 day paths: - vendor/ + - var/translations/ code_style: stage: Tests @@ -169,7 +172,27 @@ lint: cache: paths: - node_modules/ - +vue_tsc: + stage: Tests + image: node:20-alpine + before_script: + - apk add --no-cache python3 make g++ py3-setuptools + - export PYTHON="$(which python3)" + - export PATH="./node_modules/.bin:$PATH" + script: + - yarn install --ignore-optional + - yarn vue-tsc --noEmit > vue-tsc-report.txt 2>&1 || true + - cat vue-tsc-report.txt + - grep -q "error" vue-tsc-report.txt && exit 2 || exit 0 + dependencies: + - build + cache: + paths: + - node_modules/ + artifacts: + expire_in: 1 day + paths: + - vue-tsc-report.txt # psalm_tests: # stage: Tests # image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82 diff --git a/.junie/guidelines.md b/.junie/guidelines.md index df0efdfbb..5a63347d7 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -242,6 +242,7 @@ symfony composer exec phpunit # Run a specific test file symfony composer exec phpunit -- path/to/TestFile.php +symfony composer exec phpunit -- path/to/TestFile.php # Run a specific test method symfony composer exec phpunit --filter methodName path/to/TestFile.php diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..222861c34 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..d7a227605 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + // Use IntelliSense to learn about possible attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Chill Debug", + "type": "php", + "request": "launch", + "port": 9000, + "pathMappings": { + "/var/www/html": "${workspaceFolder}" + }, + "preLaunchTask": "symfony" + }, + { + "name": "Yarn Encore Dev (Watch)", + "type": "node-terminal", + "request": "launch", + "command": "yarn encore dev --watch", + "cwd": "${workspaceFolder}" + } + ], + "compounds": [ + { + "name": "Chill Debug + Yarn Encore Dev (Watch)", + "configurations": ["Chill Debug", "Yarn Encore Dev (Watch)"] + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..a652cfe03 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + "tasks": [ + { + "type": "shell", + "command": "symfony", + "args": [ + "server:start", + "--allow-http", + "--no-tls", + "--port=8000", + "--allow-all-ip", + "-d" + ], + "label": "symfony" + }, + { + "type": "shell", + "command": "yarn", + "args": ["encore", "dev", "--watch"], + "label": "webpack" + } + ] +} diff --git a/CONVENTIONS-fr.md b/CONVENTIONS-fr.md index 028971ef2..4462f4e08 100644 --- a/CONVENTIONS-fr.md +++ b/CONVENTIONS-fr.md @@ -54,7 +54,7 @@ Arborescence: - person - personvendee - household_edit_metadata - - index.js + - index.ts ``` ## Organisation des feuilles de styles diff --git a/assets/translator.ts b/assets/translator.ts index fe4e14ffa..2daba91aa 100644 --- a/assets/translator.ts +++ b/assets/translator.ts @@ -1,7 +1,12 @@ -import { trans, setLocale, setLocaleFallbacks } from "./ux-translator"; +import { + trans, + setLocale, + getLocale, + setLocaleFallbacks, +} from "./ux-translator"; -setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"}); -setLocale('fr'); +setLocaleFallbacks({ en: "fr", nl: "fr", fr: "en" }); +setLocale("fr"); -export { trans }; -export * from '../var/translations'; +export { trans, getLocale }; +export * from "../var/translations"; diff --git a/composer.json b/composer.json index 65cbd19dd..175fbc512 100644 --- a/composer.json +++ b/composer.json @@ -98,7 +98,7 @@ "require-dev": { "doctrine/doctrine-fixtures-bundle": "^3.3", "fakerphp/faker": "^1.13", - "friendsofphp/php-cs-fixer": "3.93.0", + "friendsofphp/php-cs-fixer": "^3.94", "jangregor/phpstan-prophecy": "^1.0", "nelmio/alice": "^3.8", "nikic/php-parser": "^4.15", @@ -113,13 +113,13 @@ "symfony/debug-bundle": "^5.4", "symfony/dotenv": "^5.4", "symfony/flex": "^2.4", + "symfony/loco-translation-provider": "^6.0", "symfony/maker-bundle": "^1.20", "symfony/phpunit-bridge": "^7.1", "symfony/runtime": "^5.4", "symfony/stopwatch": "^5.4", "symfony/var-dumper": "^5.4", - "symfony/web-profiler-bundle": "^5.4", - "symfony/loco-translation-provider": "^6.0" + "symfony/web-profiler-bundle": "^5.4" }, "conflict": { "symfony/symfony": "*" @@ -142,6 +142,7 @@ "Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle", "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle", "Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src", + "Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src", "Chill\\Utils\\Rector\\": "utils/rector/src" } }, diff --git a/config/bundles.php b/config/bundles.php index 72b5e22f5..7fd27811d 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -34,6 +34,7 @@ return [ Chill\ThirdPartyBundle\ChillThirdPartyBundle::class => ['all' => true], Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true], Chill\WopiBundle\ChillWopiBundle::class => ['all' => true], + Chill\TicketBundle\ChillTicketBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true], loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true], diff --git a/config/packages/chill.yaml b/config/packages/chill.yaml index 26f91feb5..121c317c9 100644 --- a/config/packages/chill.yaml +++ b/config/packages/chill.yaml @@ -34,6 +34,9 @@ chill_main: x: '%env(float:ADD_ADDRESS_MAP_CENTER_X)%' y: '%env(float:ADD_ADDRESS_MAP_CENTER_Y)%' z: '%env(float:ADD_ADDRESS_MAP_CENTER_Z)%' + homepage: + default_tab: 'MyCustoms' + display_tabs: ['MyCustoms', 'MyNotifications', 'MyAccompanyingCourses', 'MyEvaluations', 'MyTasks', 'MyWorkflows', 'MyTickets'] when@test: chill_main: diff --git a/config/packages/chill_doc_store.yaml b/config/packages/chill_doc_store.yaml index eb7f6be01..2182b17cc 100644 --- a/config/packages/chill_doc_store.yaml +++ b/config/packages/chill_doc_store.yaml @@ -1,5 +1,5 @@ chill_doc_store: - use_driver: openstack + use_driver: local_storage local_storage: storage_path: '%kernel.project_dir%/var/storage' openstack: diff --git a/config/packages/chill_ticket.yaml b/config/packages/chill_ticket.yaml new file mode 100644 index 000000000..3706d961d --- /dev/null +++ b/config/packages/chill_ticket.yaml @@ -0,0 +1,5 @@ +chill_ticket: + ticket: + person_per_ticket: one # One of "one"; "many" + response_time_exceeded_delay: PT12H + diff --git a/config/packages/doctrine_migrations_chill.yaml b/config/packages/doctrine_migrations_chill.yaml index 8b8bf539b..29acb8a49 100644 --- a/config/packages/doctrine_migrations_chill.yaml +++ b/config/packages/doctrine_migrations_chill.yaml @@ -14,6 +14,7 @@ doctrine_migrations: 'Chill\Migrations\Calendar': '@ChillCalendarBundle/migrations' 'Chill\Migrations\Budget': '@ChillBudgetBundle/migrations' 'Chill\Migrations\Report': '@ChillReportBundle/migrations' + 'Chill\Migrations\Ticket': '@ChillTicketBundle/migrations' all_or_nothing: true diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml index 4274aeec6..45802f50b 100644 --- a/config/packages/messenger.yaml +++ b/config/packages/messenger.yaml @@ -66,6 +66,7 @@ framework: 'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority 'Chill\MainBundle\Export\Messenger\RemoveExportGenerationMessage': async 'Chill\MainBundle\Notification\Email\NotificationEmailMessages\ScheduleDailyNotificationDigestMessage': async + 'Chill\TicketBundle\Messenger\PostTicketUpdateMessage': async # end of routes added by chill-bundles recipes # Route your messages to the transports # 'App\Message\YourMessage': async diff --git a/config/routes/chill_ticket.yaml b/config/routes/chill_ticket.yaml new file mode 100644 index 000000000..311a51992 --- /dev/null +++ b/config/routes/chill_ticket.yaml @@ -0,0 +1,2 @@ +chill_ticket_bundle: + resource: '@ChillTicketBundle/config/routes.yaml' diff --git a/docs/source/development/create-a-new-bundle.md b/docs/source/development/create-a-new-bundle.md index c5d1412ba..7d942ac28 100644 --- a/docs/source/development/create-a-new-bundle.md +++ b/docs/source/development/create-a-new-bundle.md @@ -1,23 +1,90 @@ -Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. - A copy of the license is included in the section entitled "GNU - Free Documentation License". +# Create a new bundle {#create-new-bundle} -###### Create a new bundle +:::: warning +::: title +Warning +::: -Create your own bundle is not a trivial task. +This part of the doc is not yet tested +:::: -The easiest way to achieve this is seems to be : +## Create a new directory with Bundle class -1. Prepare a fresh installation of the chill project, in a new directory -2. Create a new bundle in this project, in the src directory -3. Initialize a git repository **at the root bundle**, and create your initial commit. -4. Register the bundle with composer/packagist. If you do not plan to distribute your bundle with packagist, you may use a custom repository for achieve this [#f1]_ -5. Move to a development installation, made as described in the [installation-for-development` section, and add your new repository to the composer.json file -6. Work as :ref:`usual ](editing-code-and-commiting.md) +``` bash +mkdir -p src/Bundle/ChillSomeBundle/src/config +mkdir -p src/Bundle/ChillSomeBundle/src/Controller +``` - This part of the doc is not yet tested +Add a bundle file -TODO \ No newline at end of file +``` php + ['all' => true], +``` + +And import routes in `config/routes/chill_some_bundle.yaml`: + +``` yaml +chill_ticket_bundle: + resource: '@ChillSomeBundle/config/routes.yaml' +``` + +## Add the doctrine_migration namespace + +Add the namespace to `config/packages/doctrine_migrations_chill.yaml` + +``` diff +doctrine_migrations: + migrations_paths: ++ 'Chill\Some\Ticket': '@ChillSomeBundle/migrations' +``` + +## Dump autoloading + +``` bash +symfony composer dump-autoload +``` diff --git a/package.json b/package.json index a013df3da..cd4f23a07 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@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", @@ -41,6 +42,7 @@ "typescript": "^5.6.3", "typescript-eslint": "^8.13.0", "vue-loader": "^17.0.0", + "vue-tsc": "^3.1.3", "webpack": "^5.75.0", "webpack-cli": "^5.0.1" }, @@ -80,12 +82,12 @@ "dev": "encore dev", "watch": "encore dev --watch", "build": "encore production --progress", - "specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml> templates/api/specs.yaml", + "specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml src/Bundle/ChillTicketBundle/chill.api.specs.yaml> templates/api/specs.yaml", "specs-validate": "swagger-cli validate templates/api/specs.yaml", "specs-create-dir": "mkdir -p templates/api", "specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate", "version": "node --version", - "eslint": "npx eslint-baseline --fix \"src/**/*.{js,ts,vue}\"" + "eslint": "eslint-baseline --fix \"src/**/*.{js,ts,vue}\"" }, "private": true } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index cdc7468ec..d122aeb4c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -58,6 +58,10 @@ src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php + + + src/Bundle/ChillTicketBundle/tests/ + - + + diff --git a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue index 86d4a6c5a..b2d50cb6a 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue +++ b/src/Bundle/ChillCalendarBundle/Resources/public/vuejs/MyCalendarRange/Components/EditLocation.vue @@ -1,28 +1,28 @@ diff --git a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig index 38e90bd00..5502441c2 100644 --- a/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig +++ b/src/Bundle/ChillCalendarBundle/Resources/views/Calendar/_list.html.twig @@ -28,6 +28,9 @@ {% if calendar.status == 'canceled' %} {% endif %} + {% if calendar.status == 'canceled' %} + + {% endif %} {% if calendar.endDate.diff(calendar.startDate).days >= 1 %} {{ calendar.startDate|format_datetime('short', 'short') }} - {{ calendar.endDate|format_datetime('short', 'short') }} @@ -237,6 +240,10 @@ {{ 'Cancel'|trans }} +
  • + {{ 'Cancel'|trans }} +
  • {% endif %} {% if is_granted('CHILL_CALENDAR_CALENDAR_DELETE', calendar) %} diff --git a/src/Bundle/ChillCalendarBundle/Tests/Controller/CalendarControllerTest.php b/src/Bundle/ChillCalendarBundle/Tests/Controller/CalendarControllerTest.php index 31af5a921..4835ed76b 100644 --- a/src/Bundle/ChillCalendarBundle/Tests/Controller/CalendarControllerTest.php +++ b/src/Bundle/ChillCalendarBundle/Tests/Controller/CalendarControllerTest.php @@ -45,8 +45,12 @@ final class CalendarControllerTest extends WebTestCase /** * @dataProvider provideAccompanyingPeriod */ - public function testList(int $accompanyingPeriodId) + public function testList(?int $accompanyingPeriodId) { + if (null === $accompanyingPeriodId) { + $this->markTestSkipped('No AccompanyingPeriod matching criteria found in database.'); + } + $this->client->request( Request::METHOD_GET, sprintf('/fr/calendar/calendar/by-period/%d', $accompanyingPeriodId) @@ -58,8 +62,12 @@ final class CalendarControllerTest extends WebTestCase /** * @dataProvider provideAccompanyingPeriod */ - public function testNew(int $accompanyingPeriodId) + public function testNew(?int $accompanyingPeriodId) { + if (null === $accompanyingPeriodId) { + $this->markTestSkipped('No AccompanyingPeriod matching criteria found in database.'); + } + $this->client->request( Request::METHOD_GET, sprintf('/fr/calendar/calendar/new?accompanying_period_id=%d', $accompanyingPeriodId) @@ -88,6 +96,17 @@ final class CalendarControllerTest extends WebTestCase ->getQuery() ->getSingleScalarResult(); + self::ensureKernelShutdown(); + + if (0 === $nb) { + yield [null]; + + return; + } + + self::bootKernel(); + $em = self::getContainer()->get(EntityManagerInterface::class); + yield [$em->createQueryBuilder() ->from(AccompanyingPeriod::class, 'ac') ->select('ac.id') diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue b/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue index 1bcc95fd0..3a2f4fd59 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue +++ b/src/Bundle/ChillDocGeneratorBundle/Resources/public/vuejs/_components/PickTemplate.vue @@ -1,59 +1,54 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php index c849abacc..35ad2ee45 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php @@ -94,7 +94,7 @@ class StoredObject implements Document, TrackCreationInterface /** * @var Collection&Selectable */ - #[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true)] + #[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true, fetch: 'EAGER')] private Collection&Selectable $versions; /** diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts index a09c4a5f6..367ab0829 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/js/async-upload/uploader.ts @@ -6,20 +6,20 @@ const algo = "AES-CBC"; const URL_POST = "/asyncupload/temp_url/generate/post"; const keyDefinition = { - name: algo, - length: 256, + name: algo, + length: 256, }; const createFilename = (): string => { - let text = ""; - const possible = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let text = ""; + const possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (let i = 0; i < 7; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } + for (let i = 0; i < 7; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } - return text; + return text; }; /** @@ -30,59 +30,59 @@ const createFilename = (): string => { * @returns {Promise} A Promise that resolves to the newly created StoredObject. */ export const fetchNewStoredObject = async (): Promise => { - return makeFetch("POST", "/api/1.0/doc-store/stored-object/create", null); + return makeFetch("POST", "/api/1.0/doc-store/stored-object/create", null); }; export const uploadVersion = async ( - uploadFile: ArrayBuffer, - storedObject: StoredObject, + uploadFile: ArrayBuffer, + storedObject: StoredObject, ): Promise => { - const params = new URLSearchParams(); - params.append("expires_delay", "180"); - params.append("submit_delay", "180"); - const asyncData: PostStoreObjectSignature = await makeFetch( - "GET", - `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/post` + - "?" + - params.toString(), - ); - const suffix = createFilename(); - const filename = asyncData.prefix + suffix; - const formData = new FormData(); - formData.append("redirect", asyncData.redirect); - formData.append("max_file_size", asyncData.max_file_size.toString()); - formData.append("max_file_count", asyncData.max_file_count.toString()); - formData.append("expires", asyncData.expires.toString()); - formData.append("signature", asyncData.signature); - formData.append(filename, new Blob([uploadFile]), suffix); + const params = new URLSearchParams(); + params.append("expires_delay", "180"); + params.append("submit_delay", "180"); + const asyncData: PostStoreObjectSignature = await makeFetch( + "GET", + `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/post` + + "?" + + params.toString(), + ); + const suffix = createFilename(); + const filename = asyncData.prefix + suffix; + const formData = new FormData(); + formData.append("redirect", asyncData.redirect); + formData.append("max_file_size", asyncData.max_file_size.toString()); + formData.append("max_file_count", asyncData.max_file_count.toString()); + formData.append("expires", asyncData.expires.toString()); + formData.append("signature", asyncData.signature); + formData.append(filename, new Blob([uploadFile]), suffix); - const response = await window.fetch(asyncData.url, { - method: "POST", - body: formData, - }); + const response = await window.fetch(asyncData.url, { + method: "POST", + body: formData, + }); - if (!response.ok) { - console.error("Error while sending file to store", response); - throw new Error(response.statusText); - } + if (!response.ok) { + console.error("Error while sending file to store", response); + throw new Error(response.statusText); + } - return Promise.resolve(filename); + return Promise.resolve(filename); }; export const encryptFile = async ( - originalFile: ArrayBuffer, + originalFile: ArrayBuffer, ): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => { - const iv = crypto.getRandomValues(new Uint8Array(16)); - const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ - "encrypt", - "decrypt", - ]); - const exportedKey = await window.crypto.subtle.exportKey("jwk", key); - const encrypted = await window.crypto.subtle.encrypt( - { name: algo, iv: iv }, - key, - originalFile, - ); + const iv = crypto.getRandomValues(new Uint8Array(16)); + const key = await window.crypto.subtle.generateKey(keyDefinition, true, [ + "encrypt", + "decrypt", + ]); + const exportedKey = await window.crypto.subtle.exportKey("jwk", key); + const encrypted = await window.crypto.subtle.encrypt( + { name: algo, iv: iv }, + key, + originalFile, + ); - return Promise.resolve([encrypted, iv, exportedKey]); + return Promise.resolve([encrypted, iv, exportedKey]); }; diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/js/generic-doc-api.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/js/generic-doc-api.ts index c15eff711..aaff2a331 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/js/generic-doc-api.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/js/generic-doc-api.ts @@ -2,9 +2,9 @@ import { fetchResults } from "ChillMainAssets/lib/api/apiMethods"; import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc"; export function fetch_generic_docs_by_accompanying_period( - periodId: number, + periodId: number, ): Promise { - return fetchResults( - `/api/1.0/doc-store/generic-doc/by-period/${periodId}/index`, - ); + return fetchResults( + `/api/1.0/doc-store/generic-doc/by-period/${periodId}/index`, + ); } diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts index 30fe2b26a..97e0d3aa3 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/async_upload/index.ts @@ -6,117 +6,116 @@ import { _createI18n } from "../../../../../ChillMainBundle/Resources/public/vue const i18n = _createI18n({}); const startApp = ( - divElement: HTMLDivElement, - collectionEntry: null | HTMLLIElement, + divElement: HTMLDivElement, + collectionEntry: null | HTMLLIElement, ): void => { - console.log("app started", divElement); + console.log("app started", divElement); - const inputTitle = collectionEntry?.querySelector("input[type='text']"); + const inputTitle = collectionEntry?.querySelector("input[type='text']"); - const input_stored_object: HTMLInputElement | null = - divElement.querySelector("input[data-stored-object]"); - if (null === input_stored_object) { - throw new Error("input to stored object not found"); - } + const input_stored_object: HTMLInputElement | null = divElement.querySelector( + "input[data-stored-object]", + ); + if (null === input_stored_object) { + throw new Error("input to stored object not found"); + } - let existingDoc: StoredObject | null = null; - if (input_stored_object.value !== "") { - existingDoc = JSON.parse(input_stored_object.value); - } - const app_container = document.createElement("div"); - divElement.appendChild(app_container); + let existingDoc: StoredObject | null = null; + if (input_stored_object.value !== "") { + existingDoc = JSON.parse(input_stored_object.value); + } + const app_container = document.createElement("div"); + divElement.appendChild(app_container); - const app = createApp({ - template: - '', - data() { - return { - existingDoc: existingDoc, - inputTitle: inputTitle, - }; - }, - components: { - DropFileWidget, - }, - methods: { - 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; - this.$data.existingDoc.currentVersion = stored_object_version; - 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); - input_stored_object.value = ""; - this.$data.existingDoc = undefined; - console.log("collectionEntry", collectionEntry); + const app = createApp({ + template: + '', + data() { + return { + existingDoc: existingDoc, + inputTitle: inputTitle, + }; + }, + components: { + DropFileWidget, + }, + methods: { + 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; + this.$data.existingDoc.currentVersion = stored_object_version; + 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); + input_stored_object.value = ""; + this.$data.existingDoc = undefined; + console.log("collectionEntry", collectionEntry); - if (null !== collectionEntry) { - console.log("will remove collection"); - collectionEntry.remove(); - } - }, - }, - }); + if (null !== collectionEntry) { + console.log("will remove collection"); + collectionEntry.remove(); + } + }, + }, + }); - app.use(i18n).mount(app_container); + app.use(i18n).mount(app_container); }; window.addEventListener("collection-add-entry", (( - e: CustomEvent, + e: CustomEvent, ) => { - const detail = e.detail; - const divElement: null | HTMLDivElement = detail.entry.querySelector( - "div[data-stored-object]", - ); + const detail = e.detail; + const divElement: null | HTMLDivElement = detail.entry.querySelector( + "div[data-stored-object]", + ); - if (null === divElement) { - throw new Error("div[data-stored-object] not found"); - } + if (null === divElement) { + throw new Error("div[data-stored-object] not found"); + } - startApp(divElement, detail.entry); + startApp(divElement, detail.entry); }) as EventListener); window.addEventListener("DOMContentLoaded", () => { - const upload_inputs: NodeListOf = document.querySelectorAll( - "div[data-stored-object]", - ); + const upload_inputs: NodeListOf = document.querySelectorAll( + "div[data-stored-object]", + ); - upload_inputs.forEach((input: HTMLDivElement): void => { - // test for a parent to check if this is a collection entry - let collectionEntry: null | HTMLLIElement = null; - const parent = input.parentElement; - console.log("parent", parent); - if (null !== parent) { - const grandParent = parent.parentElement; - console.log("grandParent", grandParent); - if (null !== grandParent) { - if ( - grandParent.tagName.toLowerCase() === "li" && - grandParent.classList.contains("entry") - ) { - collectionEntry = grandParent as HTMLLIElement; - } - } + upload_inputs.forEach((input: HTMLDivElement): void => { + // test for a parent to check if this is a collection entry + let collectionEntry: null | HTMLLIElement = null; + const parent = input.parentElement; + console.log("parent", parent); + if (null !== parent) { + const grandParent = parent.parentElement; + console.log("grandParent", grandParent); + if (null !== grandParent) { + if ( + grandParent.tagName.toLowerCase() === "li" && + grandParent.classList.contains("entry") + ) { + collectionEntry = grandParent as HTMLLIElement; } - startApp(input, collectionEntry); - }); + } + } + startApp(input, collectionEntry); + }); }); export {}; diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts index 30b9e29d1..4ab819ddf 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/button_download/index.ts @@ -9,26 +9,26 @@ import ToastPlugin from "vue-toast-notification"; const i18n = _createI18n({}); window.addEventListener("DOMContentLoaded", function (e) { - document - .querySelectorAll("div[data-download-button-single]") - .forEach((el) => { - const storedObject = JSON.parse( - el.dataset.storedObject as string, - ) as StoredObject; - const title = el.dataset.title as string; - const app = createApp({ - components: { DownloadButton }, - data() { - return { - storedObject, - title, - classes: { btn: true, "btn-outline-primary": true }, - }; - }, - template: - '', - }); + document + .querySelectorAll("div[data-download-button-single]") + .forEach((el) => { + const storedObject = JSON.parse( + el.dataset.storedObject as string, + ) as StoredObject; + const title = el.dataset.title as string; + const app = createApp({ + components: { DownloadButton }, + data() { + return { + storedObject, + title, + classes: { btn: true, "btn-outline-primary": true }, + }; + }, + template: + '', + }); - app.use(i18n).use(ToastPlugin).mount(el); - }); + app.use(i18n).use(ToastPlugin).mount(el); + }); }); diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts index 5874e88fe..012693fac 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/module/document_action_buttons_group/index.ts @@ -2,72 +2,71 @@ import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; import DocumentActionButtonsGroup from "../../vuejs/DocumentActionButtonsGroup.vue"; import { createApp } from "vue"; import { StoredObject, StoredObjectStatusChange } from "../../types"; -import { is_object_ready } from "../../vuejs/StoredObjectButton/helpers"; import ToastPlugin from "vue-toast-notification"; const i18n = _createI18n({}); -window.addEventListener("DOMContentLoaded", function (e) { - document - .querySelectorAll("div[data-download-buttons]") - .forEach((el) => { - const app = createApp({ - components: { DocumentActionButtonsGroup }, - data() { - const datasets = el.dataset as { - filename: string; - canEdit: string; - storedObject: string; - buttonSmall: string; - davLink: string; - davLinkExpiration: string; - }; +window.addEventListener("DOMContentLoaded", function () { + document + .querySelectorAll("div[data-download-buttons]") + .forEach((el) => { + const app = createApp({ + components: { DocumentActionButtonsGroup }, + data() { + const datasets = el.dataset as { + filename: string; + canEdit: string; + storedObject: string; + buttonSmall: string; + davLink: string; + davLinkExpiration: string; + }; - const storedObject = JSON.parse( - datasets.storedObject, - ) as StoredObject, - filename = datasets.filename, - canEdit = datasets.canEdit === "1", - small = datasets.buttonSmall === "1", - davLink = - "davLink" in datasets && datasets.davLink !== "" - ? datasets.davLink - : null, - davLinkExpiration = - "davLinkExpiration" in datasets - ? Number.parseInt(datasets.davLinkExpiration) - : null; - return { - storedObject, - filename, - canEdit, - small, - davLink, - davLinkExpiration, - }; - }, - template: - '', - methods: { - onStoredObjectStatusChange: function ( - newStatus: StoredObjectStatusChange, - ): void { - this.$data.storedObject.status = newStatus.status; - this.$data.storedObject.filename = newStatus.filename; - this.$data.storedObject.type = newStatus.type; + const storedObject = JSON.parse( + datasets.storedObject, + ) as StoredObject, + filename = datasets.filename, + canEdit = datasets.canEdit === "1", + small = datasets.buttonSmall === "1", + davLink = + "davLink" in datasets && datasets.davLink !== "" + ? datasets.davLink + : null, + davLinkExpiration = + "davLinkExpiration" in datasets + ? Number.parseInt(datasets.davLinkExpiration) + : null; + return { + storedObject, + filename, + canEdit, + small, + davLink, + davLinkExpiration, + }; + }, + template: + '', + methods: { + onStoredObjectStatusChange: function ( + newStatus: StoredObjectStatusChange, + ): void { + this.$data.storedObject.status = newStatus.status; + this.$data.storedObject.filename = newStatus.filename; + this.$data.storedObject.type = newStatus.type; - // remove eventual div which inform pending status - document - .querySelectorAll( - `[data-docgen-is-pending="${this.$data.storedObject.id}"]`, - ) - .forEach(function (el) { - el.remove(); - }); - }, - }, - }); + // remove eventual div which inform pending status + document + .querySelectorAll( + `[data-docgen-is-pending="${this.$data.storedObject.id}"]`, + ) + .forEach(function (el) { + el.remove(); + }); + }, + }, + }); - app.use(i18n).use(ToastPlugin).mount(el); - }); + app.use(i18n).use(ToastPlugin).mount(el); + }); }); diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/types/generic_doc.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/types/generic_doc.ts index 34cf2501d..a571c2335 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/types/generic_doc.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/types/generic_doc.ts @@ -2,7 +2,7 @@ import { DateTime } from "ChillMainAssets/types"; import { StoredObject } from "ChillDocStoreAssets/types/index"; export interface GenericDocMetadata { - isPresent: boolean; + isPresent: boolean; } /** @@ -15,69 +15,69 @@ export interface EmptyMetadata extends GenericDocMetadata {} * Minimal Metadata for a GenericDoc with a normalizer */ export interface BaseMetadata extends GenericDocMetadata { - title: string; + title: string; } /** * A generic doc is a document attached to a Person or an AccompanyingPeriod. */ export interface GenericDoc { - type: "doc_store_generic_doc"; - uniqueKey: string; - key: string; - identifiers: { id: number }; - context: "person" | "accompanying-period"; - doc_date: DateTime; - metadata: GenericDocMetadata; - storedObject: StoredObject | null; + type: "doc_store_generic_doc"; + uniqueKey: string; + key: string; + identifiers: { id: number }; + context: "person" | "accompanying-period"; + doc_date: DateTime; + metadata: GenericDocMetadata; + storedObject: StoredObject | null; } export interface GenericDocForAccompanyingPeriod extends GenericDoc { - context: "accompanying-period"; + context: "accompanying-period"; } export function isGenericDocForAccompanyingPeriod( - doc: GenericDoc, + doc: GenericDoc, ): doc is GenericDocForAccompanyingPeriod { - return doc.context === "accompanying-period"; + return doc.context === "accompanying-period"; } export function isGenericDocWithStoredObject( - doc: GenericDoc, + doc: GenericDoc, ): doc is GenericDoc & { storedObject: StoredObject } { - return doc.storedObject !== null; + return doc.storedObject !== null; } interface BaseMetadataWithHtml extends BaseMetadata { - html: string; + html: string; } export interface GenericDocForAccompanyingCourseDocument extends GenericDocForAccompanyingPeriod { - key: "accompanying_course_document"; - metadata: BaseMetadataWithHtml; - storedObject: StoredObject; + key: "accompanying_course_document"; + metadata: BaseMetadataWithHtml; + storedObject: StoredObject; } export interface GenericDocForAccompanyingCourseActivityDocument extends GenericDocForAccompanyingPeriod { - key: "accompanying_course_activity_document"; - metadata: BaseMetadataWithHtml; - storedObject: StoredObject; + key: "accompanying_course_activity_document"; + metadata: BaseMetadataWithHtml; + storedObject: StoredObject; } export interface GenericDocForAccompanyingCourseCalendarDocument extends GenericDocForAccompanyingPeriod { - key: "accompanying_course_calendar_document"; - metadata: BaseMetadataWithHtml; - storedObject: StoredObject; + key: "accompanying_course_calendar_document"; + metadata: BaseMetadataWithHtml; + storedObject: StoredObject; } export interface GenericDocForAccompanyingCoursePersonDocument extends GenericDocForAccompanyingPeriod { - key: "person_document"; - metadata: BaseMetadataWithHtml; - storedObject: StoredObject; + key: "person_document"; + metadata: BaseMetadataWithHtml; + storedObject: StoredObject; } export interface GenericDocForAccompanyingCourseWorkEvaluationDocument extends GenericDocForAccompanyingPeriod { - key: "accompanying_period_work_evaluation_document"; - metadata: BaseMetadataWithHtml; - storedObject: StoredObject; + key: "accompanying_period_work_evaluation_document"; + metadata: BaseMetadataWithHtml; + storedObject: StoredObject; } diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/types/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/types/index.ts index 4213d971a..882d46716 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/types/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/types/index.ts @@ -4,71 +4,72 @@ import { SignedUrlGet } from "ChillDocStoreAssets/vuejs/StoredObjectButton/helpe export type StoredObjectStatus = "empty" | "ready" | "failure" | "pending"; export interface StoredObject { - id: number; - title: string | null; - uuid: string; - prefix: string; - status: StoredObjectStatus; - currentVersion: - | null - | StoredObjectVersionCreated - | StoredObjectVersionPersisted; - totalVersions: number; - datas: object; - /** @deprecated */ - creationDate: DateTime; - createdAt: DateTime | null; - createdBy: User | null; - _permissions: { - canEdit: boolean; - canSee: boolean; - }; - _links?: { - dav_link?: { - href: string; - expiration: number; - }; - downloadLink?: SignedUrlGet; + id: number; + title: string | null; + uuid: string; + prefix: string; + status: StoredObjectStatus; + type: string; + currentVersion: + | null + | StoredObjectVersionCreated + | StoredObjectVersionPersisted; + totalVersions: number; + datas: object; + /** @deprecated */ + creationDate: DateTime; + createdAt: DateTime | null; + createdBy: User | null; + _permissions: { + canEdit: boolean; + canSee: boolean; + }; + _links?: { + dav_link?: { + href: string; + expiration: number; }; + downloadLink?: SignedUrlGet; + }; } export interface StoredObjectVersion { - /** - * filename of the object in the object storage - */ - filename: string; - iv: number[]; - keyInfos: JsonWebKey; - type: string; + /** + * filename of the object in the object storage + */ + filename: string; + iv: number[]; + keyInfos: JsonWebKey; + type: string; } export interface StoredObjectVersionCreated extends StoredObjectVersion { - persisted: false; + persisted: false; } export interface StoredObjectVersionPersisted extends StoredObjectVersionCreated { - version: number; - id: number; - createdAt: DateTime | null; - createdBy: User | null; + version: number; + id: number; + createdAt: DateTime | null; + createdBy: User | null; } export interface StoredObjectStatusChange { - id: number; - filename: string; - status: StoredObjectStatus; - type: string; + id: number; + filename: string; + status: StoredObjectStatus; + type: string; } export interface StoredObjectVersionWithPointInTime extends StoredObjectVersionPersisted { - "point-in-times": StoredObjectPointInTime[]; - "from-restored": StoredObjectVersionPersisted | null; + "point-in-times": StoredObjectPointInTime[]; + "from-restored": StoredObjectVersionPersisted | null; } export interface StoredObjectPointInTime { - id: number; - byUser: User | null; - reason: "keep-before-conversion" | "keep-by-user"; + id: number; + byUser: User | null; + reason: "keep-before-conversion" | "keep-by-user"; } /** @@ -80,63 +81,63 @@ export type WopiEditButtonExecutableBeforeLeaveFunction = () => Promise; * Object containing information for performering a POST request to a swift object store */ export interface PostStoreObjectSignature { - method: "POST"; - max_file_size: number; - max_file_count: 1; - expires: number; - submit_delay: 180; - redirect: string; - prefix: string; - url: string; - signature: string; + method: "POST"; + max_file_size: number; + max_file_count: 1; + expires: number; + submit_delay: 180; + redirect: string; + prefix: string; + url: string; + signature: string; } export interface PDFPage { - index: number; - width: number; - height: number; + index: number; + width: number; + height: number; } export interface SignatureZone { - index: number | null; - x: number; - y: number; - width: number; - height: number; - PDFPage: PDFPage; + index: number | null; + x: number; + y: number; + width: number; + height: number; + PDFPage: PDFPage; } export interface Signature { - id: number; - storedObject: StoredObject; - zones: SignatureZone[]; + id: number; + storedObject: StoredObject; + zones: SignatureZone[]; } export type SignedState = - | "pending" - | "signed" - | "rejected" - | "canceled" - | "error"; + | "pending" + | "signed" + | "rejected" + | "canceled" + | "error"; export interface CheckSignature { - state: SignedState; - storedObject: StoredObject; + state: SignedState; + storedObject: StoredObject; } export type CanvasEvent = "select" | "add"; export interface ZoomLevel { - id: number; - zoom: number; - label: { - fr?: string; - nl?: string; - }; + id: number; + zoom: number; + label: { + fr?: string; + nl?: string; + }; } export interface GenericDoc { - type: "doc_store_generic_doc"; - key: string; - context: "person" | "accompanying-period"; - doc_date: DateTime; + type: "doc_store_generic_doc"; + key: string; + context: "person" | "accompanying-period"; + doc_date: DateTime; } diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue index 85548f6d3..baa99c5ab 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentActionButtonsGroup.vue @@ -1,67 +1,65 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue index ea195da4b..3021de36e 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/App.vue @@ -1,355 +1,338 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts index a46257b5e..e3ace5b40 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DocumentSignature/index.ts @@ -4,36 +4,36 @@ import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; import App from "./App.vue"; const appMessages = { - fr: { - 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", - }, + fr: { + 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", + }, }; const i18n = _createI18n(appMessages); const app = createApp({ - template: ``, + template: ``, }) - .use(i18n) - .component("app", App) - .mount("#document-signature"); + .use(i18n) + .component("app", App) + .mount("#document-signature"); diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFile.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFile.vue index 017629ee4..2e1dd158d 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFile.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFile.vue @@ -1,208 +1,205 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue index ef6846828..5c604426c 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileModal.vue @@ -7,24 +7,24 @@ import { useToast } from "vue-toast-notification"; import { DOCUMENT_ADD, trans } from "translator"; interface DropFileConfig { - allowRemove: boolean; - existingDoc?: StoredObject; + allowRemove: boolean; + existingDoc?: StoredObject; } const props = withDefaults(defineProps(), { - allowRemove: false, + allowRemove: false, }); const emit = defineEmits<{ - ( - e: "addDocument", - { - stored_object: StoredObject, - stored_object_version: StoredObjectVersion, - file_name: string, - }, - ): void; - (e: "removeDocument"): void; + ( + e: "addDocument", + payload: { + stored_object: StoredObject; + stored_object_version: StoredObjectVersion; + file_name: string; + }, + ): void; + (e: "removeDocument"): void; }>(); const $toast = useToast(); @@ -34,65 +34,65 @@ const state = reactive({ showModal: false }); const modalClasses = { "modal-dialog-centered": true, "modal-md": true }; const buttonState = computed<"add" | "replace">(() => { - if (props.existingDoc === undefined || props.existingDoc === null) { - return "add"; - } + if (props.existingDoc === undefined || props.existingDoc === null) { + return "add"; + } - return "replace"; + return "replace"; }); function onAddDocument({ - stored_object, - stored_object_version, - file_name, + stored_object, + stored_object_version, + file_name, }: { - stored_object: StoredObject; - stored_object_version: StoredObjectVersion; - file_name: string; + 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, file_name }); - state.showModal = false; + const message = + buttonState.value === "add" ? "Document ajouté" : "Document remplacé"; + $toast.success(message); + emit("addDocument", { stored_object_version, stored_object, file_name }); + state.showModal = false; } function onRemoveDocument(): void { - emit("removeDocument"); + emit("removeDocument"); } function openModal(): void { - state.showModal = true; + state.showModal = true; } function closeModal(): void { - state.showModal = false; + state.showModal = false; } diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue index 54f8ddf0c..bb8b1bafe 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/DropFileWidget/DropFileWidget.vue @@ -1,101 +1,96 @@ + + - - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/FileIcon.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/FileIcon.vue index 5260095fa..8556524ad 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/FileIcon.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/FileIcon.vue @@ -1,46 +1,46 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue index b6c6a496f..8cb60bc12 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/ConvertButton.vue @@ -1,28 +1,28 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DesktopEditButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DesktopEditButton.vue index 2b74f76bd..9bb3c4c6f 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DesktopEditButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DesktopEditButton.vue @@ -1,15 +1,69 @@ + + + + - - - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue index d28b49cdf..3087bc362 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/DownloadButton.vue @@ -1,141 +1,142 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue index 71a8d42ea..c11f249cf 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton.vue @@ -1,20 +1,34 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue index ac4bf4591..7205f30a6 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue @@ -1,26 +1,45 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue index bd8502e96..f39bc817e 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue @@ -1,8 +1,75 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue index b9535ca2e..3cf3c1e11 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue @@ -1,56 +1,56 @@ + + - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue index 41ae8e978..83b820107 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/RestoreVersionButton.vue @@ -1,17 +1,27 @@ + + - - diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/api.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/api.ts index cb7581eab..b003e26b7 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/api.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/HistoryButton/api.ts @@ -1,33 +1,33 @@ import { - StoredObject, - StoredObjectVersionPersisted, - StoredObjectVersionWithPointInTime, + StoredObject, + StoredObjectVersionPersisted, + StoredObjectVersionWithPointInTime, } from "../../../types"; import { - fetchResults, - makeFetch, + fetchResults, + makeFetch, } from "../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; export const get_versions = async ( - storedObject: StoredObject, + storedObject: StoredObject, ): Promise => { - const versions = await fetchResults( - `/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`, - ); + const versions = await fetchResults( + `/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`, + ); - return versions.sort( - ( - a: StoredObjectVersionWithPointInTime, - b: StoredObjectVersionWithPointInTime, - ) => b.version - a.version, - ); + return versions.sort( + ( + a: StoredObjectVersionWithPointInTime, + b: StoredObjectVersionWithPointInTime, + ) => b.version - a.version, + ); }; export const restore_version = async ( - version: StoredObjectVersionPersisted, + version: StoredObjectVersionPersisted, ): Promise => { - return await makeFetch( - "POST", - `/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`, - ); + return await makeFetch( + "POST", + `/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`, + ); }; diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue index b57c1f188..97073e4e1 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/WopiEditButton.vue @@ -1,29 +1,27 @@ diff --git a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts index ff1e6219d..8dbad34b7 100644 --- a/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts +++ b/src/Bundle/ChillDocStoreBundle/Resources/public/vuejs/StoredObjectButton/helpers.ts @@ -1,235 +1,228 @@ import { - StoredObject, - StoredObjectStatus, - StoredObjectStatusChange, - StoredObjectVersion, + StoredObject, + StoredObjectStatus, + StoredObjectStatusChange, + StoredObjectVersion, } from "../../types"; import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods"; const MIMES_EDIT = new Set([ - "application/vnd.ms-powerpoint", - "application/vnd.ms-excel", - "application/vnd.oasis.opendocument.text", - "application/vnd.oasis.opendocument.text-flat-xml", - "application/vnd.oasis.opendocument.spreadsheet", - "application/vnd.oasis.opendocument.spreadsheet-flat-xml", - "application/vnd.oasis.opendocument.presentation", - "application/vnd.oasis.opendocument.presentation-flat-xml", - "application/vnd.oasis.opendocument.graphics", - "application/vnd.oasis.opendocument.graphics-flat-xml", - "application/vnd.oasis.opendocument.chart", - "application/msword", - "application/vnd.ms-excel", - "application/vnd.ms-powerpoint", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "application/vnd.ms-word.document.macroEnabled.12", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "application/vnd.ms-excel.sheet.binary.macroEnabled.12", - "application/vnd.ms-excel.sheet.macroEnabled.12", - "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "application/vnd.ms-powerpoint.presentation.macroEnabled.12", - "application/x-dif-document", - "text/spreadsheet", - "text/csv", - "application/x-dbase", - "text/rtf", - "text/plain", - "application/vnd.openxmlformats-officedocument.presentationml.slideshow", + "application/vnd.ms-powerpoint", + "application/vnd.ms-excel", + "application/vnd.oasis.opendocument.text", + "application/vnd.oasis.opendocument.text-flat-xml", + "application/vnd.oasis.opendocument.spreadsheet", + "application/vnd.oasis.opendocument.spreadsheet-flat-xml", + "application/vnd.oasis.opendocument.presentation", + "application/vnd.oasis.opendocument.presentation-flat-xml", + "application/vnd.oasis.opendocument.graphics", + "application/vnd.oasis.opendocument.graphics-flat-xml", + "application/vnd.oasis.opendocument.chart", + "application/msword", + "application/vnd.ms-excel", + "application/vnd.ms-powerpoint", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.ms-word.document.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.ms-excel.sheet.binary.macroEnabled.12", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/vnd.ms-powerpoint.presentation.macroEnabled.12", + "application/x-dif-document", + "text/spreadsheet", + "text/csv", + "application/x-dbase", + "text/rtf", + "text/plain", + "application/vnd.openxmlformats-officedocument.presentationml.slideshow", ]); const MIMES_VIEW = new Set([ - ...MIMES_EDIT, - [ - "image/svg+xml", - "application/vnd.sun.xml.writer", - "application/vnd.sun.xml.calc", - "application/vnd.sun.xml.impress", - "application/vnd.sun.xml.draw", - "application/vnd.sun.xml.writer.global", - "application/vnd.sun.xml.writer.template", - "application/vnd.sun.xml.calc.template", - "application/vnd.sun.xml.impress.template", - "application/vnd.sun.xml.draw.template", - "application/vnd.oasis.opendocument.text-master", - "application/vnd.oasis.opendocument.text-template", - "application/vnd.oasis.opendocument.text-master-template", - "application/vnd.oasis.opendocument.spreadsheet-template", - "application/vnd.oasis.opendocument.presentation-template", - "application/vnd.oasis.opendocument.graphics-template", - "application/vnd.ms-word.template.macroEnabled.12", - "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "application/vnd.ms-excel.template.macroEnabled.12", - "application/vnd.openxmlformats-officedocument.presentationml.template", - "application/vnd.ms-powerpoint.template.macroEnabled.12", - "application/vnd.wordperfect", - "application/x-aportisdoc", - "application/x-hwp", - "application/vnd.ms-works", - "application/x-mswrite", - "application/vnd.lotus-1-2-3", - "image/cgm", - "image/vnd.dxf", - "image/x-emf", - "image/x-wmf", - "application/coreldraw", - "application/vnd.visio2013", - "application/vnd.visio", - "application/vnd.ms-visio.drawing", - "application/x-mspublisher", - "application/x-sony-bbeb", - "application/x-gnumeric", - "application/macwriteii", - "application/x-iwork-numbers-sffnumbers", - "application/vnd.oasis.opendocument.text-web", - "application/x-pagemaker", - "application/x-fictionbook+xml", - "application/clarisworks", - "image/x-wpg", - "application/x-iwork-pages-sffpages", - "application/x-iwork-keynote-sffkey", - "application/x-abiword", - "image/x-freehand", - "application/vnd.sun.xml.chart", - "application/x-t602", - "image/bmp", - "image/png", - "image/gif", - "image/tiff", - "image/jpg", - "image/jpeg", - "application/pdf", - ], + ...MIMES_EDIT, + [ + "image/svg+xml", + "application/vnd.sun.xml.writer", + "application/vnd.sun.xml.calc", + "application/vnd.sun.xml.impress", + "application/vnd.sun.xml.draw", + "application/vnd.sun.xml.writer.global", + "application/vnd.sun.xml.writer.template", + "application/vnd.sun.xml.calc.template", + "application/vnd.sun.xml.impress.template", + "application/vnd.sun.xml.draw.template", + "application/vnd.oasis.opendocument.text-master", + "application/vnd.oasis.opendocument.text-template", + "application/vnd.oasis.opendocument.text-master-template", + "application/vnd.oasis.opendocument.spreadsheet-template", + "application/vnd.oasis.opendocument.presentation-template", + "application/vnd.oasis.opendocument.graphics-template", + "application/vnd.ms-word.template.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.spreadsheetml.template", + "application/vnd.ms-excel.template.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.presentationml.template", + "application/vnd.ms-powerpoint.template.macroEnabled.12", + "application/vnd.wordperfect", + "application/x-aportisdoc", + "application/x-hwp", + "application/vnd.ms-works", + "application/x-mswrite", + "application/vnd.lotus-1-2-3", + "image/cgm", + "image/vnd.dxf", + "image/x-emf", + "image/x-wmf", + "application/coreldraw", + "application/vnd.visio2013", + "application/vnd.visio", + "application/vnd.ms-visio.drawing", + "application/x-mspublisher", + "application/x-sony-bbeb", + "application/x-gnumeric", + "application/macwriteii", + "application/x-iwork-numbers-sffnumbers", + "application/vnd.oasis.opendocument.text-web", + "application/x-pagemaker", + "application/x-fictionbook+xml", + "application/clarisworks", + "image/x-wpg", + "application/x-iwork-pages-sffpages", + "application/x-iwork-keynote-sffkey", + "application/x-abiword", + "image/x-freehand", + "application/vnd.sun.xml.chart", + "application/x-t602", + "image/bmp", + "image/png", + "image/gif", + "image/tiff", + "image/jpg", + "image/jpeg", + "application/pdf", + ], ]); export interface SignedUrlGet { - method: "GET" | "HEAD"; - url: string; - expires: number; - object_name: string; + method: "GET" | "HEAD"; + url: string; + expires: number; + object_name: string; } function is_extension_editable(mimeType: string): boolean { - return MIMES_EDIT.has(mimeType); + return MIMES_EDIT.has(mimeType); } function is_extension_viewable(mimeType: string): boolean { - return MIMES_VIEW.has(mimeType); + return MIMES_VIEW.has(mimeType); } function build_convert_link(uuid: string) { - return `/chill/wopi/convert/${uuid}`; + return `/chill/wopi/convert/${uuid}`; } function build_download_info_link( - storedObject: StoredObject, - atVersion: null | StoredObjectVersion, + storedObject: StoredObject, + atVersion: null | StoredObjectVersion, ): string { - const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`; + const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`; - if (null !== atVersion) { - const params = new URLSearchParams({ version: atVersion.filename }); + if (null !== atVersion) { + const params = new URLSearchParams({ version: atVersion.filename }); - return url + "?" + params.toString(); - } + return url + "?" + params.toString(); + } - return url; + return url; } async function download_info_link( - storedObject: StoredObject, - atVersion: null | StoredObjectVersion, + storedObject: StoredObject, + atVersion: null | StoredObjectVersion, ): Promise { - return makeFetch("GET", build_download_info_link(storedObject, atVersion)); + return makeFetch("GET", build_download_info_link(storedObject, atVersion)); } function build_wopi_editor_link(uuid: string, returnPath?: string) { - if (returnPath === undefined) { - returnPath = - window.location.pathname + - window.location.search + - window.location.hash; - } + if (returnPath === undefined) { + returnPath = + window.location.pathname + window.location.search + window.location.hash; + } - return ( - `/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath) - ); + return ( + `/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath) + ); } function download_doc(url: string): Promise { - return window.fetch(url).then((r) => { - if (r.ok) { - return r.blob(); - } + return window.fetch(url).then((r) => { + if (r.ok) { + return r.blob(); + } - throw new Error("Could not download document"); - }); + throw new Error("Could not download document"); + }); } async function download_and_decrypt_doc( - storedObject: StoredObject, - atVersion: null | StoredObjectVersion, + storedObject: StoredObject, + atVersion: null | StoredObjectVersion, ): Promise { - const algo = "AES-CBC"; + const algo = "AES-CBC"; - const atVersionToDownload = atVersion ?? storedObject.currentVersion; + const atVersionToDownload = atVersion ?? storedObject.currentVersion; - if (null === atVersionToDownload) { - throw new Error("no version associated to stored object"); - } + if (null === atVersionToDownload) { + throw new Error("no version associated to stored object"); + } - // sometimes, the downloadInfo may be embedded into the storedObject - console.log("storedObject", storedObject); - let downloadInfo; - if ( - typeof storedObject._links !== "undefined" && - typeof storedObject._links.downloadLink !== "undefined" - ) { - downloadInfo = storedObject._links.downloadLink; - } else { - downloadInfo = await download_info_link( - storedObject, - atVersionToDownload, - ); - } + let downloadInfo; + if ( + typeof storedObject._links !== "undefined" && + typeof storedObject._links.downloadLink !== "undefined" + ) { + downloadInfo = storedObject._links.downloadLink; + } else { + downloadInfo = await download_info_link(storedObject, atVersionToDownload); + } - const rawResponse = await window.fetch(downloadInfo.url); + const rawResponse = await window.fetch(downloadInfo.url); - if (!rawResponse.ok) { - throw new Error( - "error while downloading raw file " + - rawResponse.status + - " " + - rawResponse.statusText, - ); - } + if (!rawResponse.ok) { + throw new Error( + "error while downloading raw file " + + rawResponse.status + + " " + + rawResponse.statusText, + ); + } - if (atVersionToDownload.iv.length === 0) { - return rawResponse.blob(); - } + if (atVersionToDownload.iv.length === 0) { + return rawResponse.blob(); + } - const rawBuffer = await rawResponse.arrayBuffer(); - try { - const key = await window.crypto.subtle.importKey( - "jwk", - atVersionToDownload.keyInfos, - { name: algo }, - false, - ["decrypt"], - ); - const iv = Uint8Array.from(atVersionToDownload.iv); - const decrypted = await window.crypto.subtle.decrypt( - { name: algo, iv: iv }, - key, - rawBuffer, - ); + const rawBuffer = await rawResponse.arrayBuffer(); + try { + const key = await window.crypto.subtle.importKey( + "jwk", + atVersionToDownload.keyInfos, + { name: algo }, + false, + ["decrypt"], + ); + const iv = Uint8Array.from(atVersionToDownload.iv); + const decrypted = await window.crypto.subtle.decrypt( + { name: algo, iv: iv }, + key, + rawBuffer, + ); - return Promise.resolve(new Blob([decrypted])); - } catch (e) { - console.error("encounter error while keys and decrypt operations"); - console.error(e); + return Promise.resolve(new Blob([decrypted])); + } catch (e) { + console.error("encounter error while keys and decrypt operations"); + console.error(e); - throw e; - } + throw e; + } } /** @@ -239,48 +232,46 @@ async function download_and_decrypt_doc( * storage. */ async function download_doc_as_pdf(storedObject: StoredObject): Promise { - if (null === storedObject.currentVersion) { - throw new Error("the stored object does not count any version"); - } + if (null === storedObject.currentVersion) { + throw new Error("the stored object does not count any version"); + } - if (storedObject.currentVersion?.type === "application/pdf") { - return download_and_decrypt_doc( - storedObject, - storedObject.currentVersion, - ); - } + if (storedObject.currentVersion?.type === "application/pdf") { + return download_and_decrypt_doc(storedObject, storedObject.currentVersion); + } - const convertLink = build_convert_link(storedObject.uuid); - const response = await fetch(convertLink); + const convertLink = build_convert_link(storedObject.uuid); + const response = await fetch(convertLink); - if (!response.ok) { - throw new Error("Could not convert the document: " + response.status); - } + if (!response.ok) { + throw new Error("Could not convert the document: " + response.status); + } - return response.blob(); + return response.blob(); } async function is_object_ready( - storedObject: StoredObject, + storedObject: StoredObject, ): Promise { - const new_status_response = await window.fetch( - `/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`, - ); + const new_status_response = await window.fetch( + `/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`, + ); - if (!new_status_response.ok) { - throw new Error("could not fetch the new status"); - } + if (!new_status_response.ok) { + throw new Error("could not fetch the new status"); + } - return await new_status_response.json(); + return await new_status_response.json(); } export { - build_convert_link, - build_wopi_editor_link, - download_and_decrypt_doc, - download_doc, - download_doc_as_pdf, - is_extension_editable, - is_extension_viewable, - is_object_ready, + build_convert_link, + build_wopi_editor_link, + download_info_link, + download_and_decrypt_doc, + download_doc, + download_doc_as_pdf, + is_extension_editable, + is_extension_viewable, + is_object_ready, }; diff --git a/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectVersionNormalizer.php b/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectVersionNormalizer.php index 4b18989cb..aa35f2052 100644 --- a/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectVersionNormalizer.php +++ b/src/Bundle/ChillDocStoreBundle/Serializer/Normalizer/StoredObjectVersionNormalizer.php @@ -43,11 +43,17 @@ class StoredObjectVersionNormalizer implements NormalizerInterface, NormalizerAw 'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, [...$context, UserNormalizer::AT_DATE => $object->getCreatedAt()]), ]; - if (in_array(self::WITH_POINT_IN_TIMES_CONTEXT, $context[AbstractNormalizer::GROUPS] ?? [], true)) { + $normalizationGroups = $context[AbstractNormalizer::GROUPS] ?? []; + + if (is_string($normalizationGroups)) { + $normalizationGroups = [$normalizationGroups]; + } + + if (in_array(self::WITH_POINT_IN_TIMES_CONTEXT, $normalizationGroups, true)) { $data['point-in-times'] = $this->normalizer->normalize($object->getPointInTimes(), $format, $context); } - if (in_array(self::WITH_RESTORED_CONTEXT, $context[AbstractNormalizer::GROUPS] ?? [], true)) { + if (in_array(self::WITH_RESTORED_CONTEXT, $normalizationGroups, true)) { $data['from-restored'] = $this->normalizer->normalize($object->getCreatedFrom(), $format, [AbstractNormalizer::GROUPS => ['read']]); } diff --git a/src/Bundle/ChillEventBundle/Resources/public/vuejs/App.vue b/src/Bundle/ChillEventBundle/Resources/public/vuejs/App.vue index 89158d742..02d64f838 100644 --- a/src/Bundle/ChillEventBundle/Resources/public/vuejs/App.vue +++ b/src/Bundle/ChillEventBundle/Resources/public/vuejs/App.vue @@ -1,14 +1,14 @@ diff --git a/src/Bundle/ChillJobBundle/src/Entity/Immersion.php b/src/Bundle/ChillJobBundle/src/Entity/Immersion.php index 5ae19a365..80d05062e 100644 --- a/src/Bundle/ChillJobBundle/src/Entity/Immersion.php +++ b/src/Bundle/ChillJobBundle/src/Entity/Immersion.php @@ -19,7 +19,6 @@ use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Address; use libphonenumber\PhoneNumber; use Symfony\Component\Validator\Constraints as Assert; -use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; /** * Immersion. @@ -86,14 +85,14 @@ class Immersion implements \Stringable * @Assert\NotBlank() */ #[ORM\Column(name: 'tuteurPhoneNumber', type: 'phone_number', nullable: true)] - #[PhonenumberConstraint(type: 'any')] + #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber] private ?PhoneNumber $tuteurPhoneNumber = null; #[ORM\Column(name: 'structureAccName', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)] private ?string $structureAccName = null; #[ORM\Column(name: 'structureAccPhonenumber', type: 'phone_number', nullable: true)] - #[PhonenumberConstraint(type: 'any')] + #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber] private ?PhoneNumber $structureAccPhonenumber = null; #[ORM\Column(name: 'structureAccEmail', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)] diff --git a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php index 061f8e55a..d0c8e4ac7 100644 --- a/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php +++ b/src/Bundle/ChillMainBundle/Action/User/UpdateProfile/UpdateProfileCommand.php @@ -13,7 +13,6 @@ namespace Chill\MainBundle\Action\User\UpdateProfile; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Notification\NotificationFlagManager; -use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use libphonenumber\PhoneNumber; use Symfony\Component\Validator\Constraints as Assert; @@ -22,7 +21,7 @@ final class UpdateProfileCommand public array $notificationFlags = []; public function __construct( - #[PhonenumberConstraint] + #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber] public ?PhoneNumber $phonenumber, #[Assert\Choice(choices: ['fr', 'nl'], message: 'Locale must be either "fr" or "nl"')] public string $locale = 'fr', diff --git a/src/Bundle/ChillMainBundle/ChillMainBundle.php b/src/Bundle/ChillMainBundle/ChillMainBundle.php index 4a059662c..8d2969ee3 100644 --- a/src/Bundle/ChillMainBundle/ChillMainBundle.php +++ b/src/Bundle/ChillMainBundle/ChillMainBundle.php @@ -14,7 +14,6 @@ namespace Chill\MainBundle; use Chill\MainBundle\Cron\CronJobInterface; use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass; -use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass; @@ -70,7 +69,6 @@ class ChillMainBundle extends Bundle $container->addCompilerPass(new TimelineCompilerClass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new WidgetsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new NotificationCounterCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); - $container->addCompilerPass(new MenuCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new ACLFlagsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); $container->addCompilerPass(new CRUDControllerCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0); } diff --git a/src/Bundle/ChillMainBundle/Command/OverrideTranslationCommand.php b/src/Bundle/ChillMainBundle/Command/OverrideTranslationCommand.php new file mode 100644 index 000000000..0f8070456 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Command/OverrideTranslationCommand.php @@ -0,0 +1,115 @@ +setName('chill:main:override_translation'); + parent::__construct(); + } + + protected function configure(): void + { + $this + ->setDescription('Generate a translation catalogue with translation remplacements based on replacements provided in a YAML file.') + ->addArgument('locale', InputArgument::REQUIRED, 'The locale to process (e.g. fr, en).') + ->addArgument('overrides', InputArgument::REQUIRED, 'Path to the overrides YAML file (list of {from, to}).'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $locale = (string) $input->getArgument('locale'); + $overridesPath = (string) $input->getArgument('overrides'); + + $catalogue = $this->loadCatalogue($locale); + $overrides = $this->loadOverrides($overridesPath); + + + $toOverrideCatalogue = new MessageCatalogue($locale); + foreach ($catalogue->getDomains() as $domain) { + // hack: we have to replace the suffix ".intl-icu" by "+intl-ic" + $domain = str_replace('.intl-icu', '+intl-icu', $domain); + foreach ($catalogue->all($domain) as $key => $translation) { + foreach ($overrides as $changes) { + $from = $changes['from']; + $to = $changes['to']; + + if (is_string($translation) && str_contains($translation, $from)) { + $newTranslation = strtr($translation, [$from => $to]); + $toOverrideCatalogue->set($key, $newTranslation, $domain); + $translation = $newTranslation; + } + } + } + } + + /** @var KernelInterface $kernel */ + /* @phpstan-ignore-next-line */ + $kernel = $this->getApplication()->getKernel(); + $outputDir = rtrim($kernel->getProjectDir(), '/').'/translations'; + if (!is_dir($outputDir)) { + @mkdir($outputDir, 0775, true); + } + + // Writer expects the 'path' option to be a directory; it will create the proper file name + $this->writer->write($toOverrideCatalogue, 'yaml', ['path' => $outputDir]); + + $output->writeln(sprintf('Override catalogue written to %s (domain: messages, locale: %s).', $outputDir, $locale)); + + return Command::SUCCESS; + } + + /** + * @return list + */ + private function loadOverrides(string $path): array + { + return Yaml::parseFile($path); + } + + private function loadCatalogue(string $locale): MessageCatalogue + { + /** @var KernelInterface $kernel */ + /* @phpstan-ignore-next-line */ + $kernel = $this->getApplication()->getKernel(); + + // collect path for translations + $transPaths = []; + foreach ($kernel->getBundles() as $bundle) { + $bundleDir = $bundle->getPath(); + $transPaths[] = is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundle->getPath().'/translations'; + } + + $currentCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->reader->read($path, $currentCatalogue); + } + } + + return $currentCatalogue; + } +} diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index ebb28d2e7..856c0ae09 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -84,6 +84,7 @@ use Chill\MainBundle\Security\Authorization\ChillExportVoter; use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter; use Misd\PhoneNumberBundle\Doctrine\DBAL\Types\PhoneNumberType; use Ramsey\Uuid\Doctrine\UuidType; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; @@ -210,6 +211,15 @@ class ChillMainExtension extends Extension implements $config['top_banner'] ?? [] ); + if (!in_array($config['homepage']['default_tab'], $config['homepage']['display_tabs'], true)) { + throw new InvalidConfigurationException('The chill_main.homepage.default_tab must be included in chill_main.homepage.display_tabs'); + } + + $container->setParameter( + 'chill_main.homepage', + $config['homepage'] + ); + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config')); $loader->load('services.yaml'); $loader->load('services/doctrine.yaml'); @@ -241,7 +251,7 @@ class ChillMainExtension extends Extension implements // $this->configureSms($config['short_messages'], $container, $loader); } - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { $this->prependNotifierTexterWithLegacyData($container); @@ -256,6 +266,7 @@ class ChillMainExtension extends Extension implements 'available_languages' => $config['available_languages'], 'add_address' => $config['add_address'], 'chill_main_config' => $config, + 'homepage_widget_config' => $config['homepage'], ], 'form_themes' => ['@ChillMain/Form/fields.html.twig'], ]; diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php index a3247d88e..712ebc004 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php @@ -325,6 +325,17 @@ class Configuration implements ConfigurationInterface ->end() ->end(); + /* @phpstan-ignore-next-line */ + $rootNode->children() + ->arrayNode('homepage')->addDefaultsIfNotSet() + ->children() + ->scalarNode('default_tab')->defaultValue('MyCustoms')->end() + ->arrayNode('display_tabs') + ->info('List of tabs to display on the homepage.') + ->defaultValue(['MyCustoms', 'MyNotifications', 'MyAccompanyingCourses', 'MyEvaluations', 'MyTasks', 'MyWorkflows']) + ->scalarPrototype()->end() + ->end(); + return $treeBuilder; } } diff --git a/src/Bundle/ChillMainBundle/Entity/Location.php b/src/Bundle/ChillMainBundle/Entity/Location.php index 5705e6a1a..e2605f9a1 100644 --- a/src/Bundle/ChillMainBundle/Entity/Location.php +++ b/src/Bundle/ChillMainBundle/Entity/Location.php @@ -14,9 +14,9 @@ namespace Chill\MainBundle\Entity; use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; use Chill\MainBundle\Repository\LocationRepository; -use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use Doctrine\ORM\Mapping as ORM; use libphonenumber\PhoneNumber; +use Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber as MisdPhoneNumberConstraint; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; @@ -67,12 +67,12 @@ class Location implements TrackCreationInterface, TrackUpdateInterface #[Serializer\Groups(['read', 'write', 'docgen:read'])] #[ORM\Column(type: 'phone_number', nullable: true)] - #[PhonenumberConstraint(type: 'any')] + #[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::ANY])] private ?PhoneNumber $phonenumber1 = null; #[Serializer\Groups(['read', 'write', 'docgen:read'])] #[ORM\Column(type: 'phone_number', nullable: true)] - #[PhonenumberConstraint(type: 'any')] + #[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::ANY])] private ?PhoneNumber $phonenumber2 = null; #[Serializer\Groups(['read'])] diff --git a/src/Bundle/ChillMainBundle/Entity/PostalCode.php b/src/Bundle/ChillMainBundle/Entity/PostalCode.php index d6ffe763f..29218475c 100644 --- a/src/Bundle/ChillMainBundle/Entity/PostalCode.php +++ b/src/Bundle/ChillMainBundle/Entity/PostalCode.php @@ -99,10 +99,8 @@ class PostalCode implements TrackUpdateInterface, TrackCreationInterface /** * Get country. - * - * @return Country */ - public function getCountry() + public function getCountry(): ?Country { return $this->country; } diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php index a273fefd7..1fddfe445 100644 --- a/src/Bundle/ChillMainBundle/Entity/User.php +++ b/src/Bundle/ChillMainBundle/Entity/User.php @@ -23,7 +23,6 @@ use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint; use Symfony\Component\Validator\Constraints as Assert; /** @@ -119,7 +118,7 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter * The user's mobile phone number. */ #[ORM\Column(type: 'phone_number', nullable: true)] - #[PhonenumberConstraint] + #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber] private ?PhoneNumber $phonenumber = null; /** @@ -134,6 +133,9 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter #[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 5, nullable: false, options: ['default' => 'fr'])] private string $locale = 'fr'; + #[ORM\ManyToMany(targetEntity: UserGroup::class, mappedBy: 'users')] + private Collection&Selectable $groupsAsMember; + /** * User constructor. */ @@ -142,6 +144,7 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter $this->groupCenters = new ArrayCollection(); $this->scopeHistories = new ArrayCollection(); $this->jobHistories = new ArrayCollection(); + $this->groupsAsMember = new ArrayCollection(); } public function __toString(): string @@ -171,6 +174,32 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter return $this->absenceEnd; } + public function addGroupAsMember(UserGroup $userGroup): self + { + if (!$this->groupsAsMember->contains($userGroup)) { + $this->groupsAsMember->add($userGroup); + } + + return $this; + } + + public function removeGroupAsMember(UserGroup $userGroup): self + { + if ($this->groupsAsMember->contains($userGroup)) { + $this->groupsAsMember->removeElement($userGroup); + } + + return $this; + } + + /** + * @return Selectable&Collection + */ + public function getGroupsAsMember(): Collection&Selectable + { + return $this->groupsAsMember; + } + /** * Get attributes. * diff --git a/src/Bundle/ChillMainBundle/Entity/UserGroup.php b/src/Bundle/ChillMainBundle/Entity/UserGroup.php index f6586d4c9..2977d1b28 100644 --- a/src/Bundle/ChillMainBundle/Entity/UserGroup.php +++ b/src/Bundle/ChillMainBundle/Entity/UserGroup.php @@ -54,7 +54,7 @@ class UserGroup /** * @var Collection&Selectable */ - #[ORM\ManyToMany(targetEntity: User::class)] + #[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'groupsAsMember')] #[ORM\JoinTable(name: 'chill_main_user_group_user')] private Collection&Selectable $users; @@ -129,6 +129,7 @@ class UserGroup { if (!$this->users->contains($user)) { $this->users[] = $user; + $user->addGroupAsMember($this); } return $this; @@ -138,6 +139,7 @@ class UserGroup { if ($this->users->contains($user)) { $this->users->removeElement($user); + $user->removeGroupAsMember($this); } return $this; diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php index 27ee7f44c..b8105fe5f 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php @@ -27,6 +27,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\Translation\TranslatableInterface; use Symfony\Contracts\Translation\TranslatorInterface; +// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff + /** * Create a CSV List for the export. */ diff --git a/src/Bundle/ChillMainBundle/Phonenumber/PhoneNumberHelperInterface.php b/src/Bundle/ChillMainBundle/Phonenumber/PhoneNumberHelperInterface.php index c7cf1ddfd..ca0e3e8e0 100644 --- a/src/Bundle/ChillMainBundle/Phonenumber/PhoneNumberHelperInterface.php +++ b/src/Bundle/ChillMainBundle/Phonenumber/PhoneNumberHelperInterface.php @@ -31,6 +31,8 @@ interface PhoneNumberHelperInterface /** * Return true if the validation is configured and available. + * + * @deprecated this is an internal behaviour of the helper and should not be taken into account outside of the implementation */ public function isPhonenumberValidationConfigured(): bool; diff --git a/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php b/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php index a73c3c71c..9b103f275 100644 --- a/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php +++ b/src/Bundle/ChillMainBundle/Phonenumber/PhonenumberHelper.php @@ -76,6 +76,24 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface ->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']); } + /** + * @throws NumberParseException + */ + public function parse(string $phoneNumber): PhoneNumber + { + $sanitizedPhoneNumber = $phoneNumber; + + if (str_starts_with($sanitizedPhoneNumber, '00')) { + $sanitizedPhoneNumber = '+'.substr($sanitizedPhoneNumber, 2, null); + } + + if (!str_starts_with($sanitizedPhoneNumber, '+') && !str_starts_with($sanitizedPhoneNumber, '0')) { + $sanitizedPhoneNumber = '+'.$sanitizedPhoneNumber; + } + + return $this->phoneNumberUtil->parse($sanitizedPhoneNumber, $this->config['default_carrier_code']); + } + /** * Get type (mobile, landline, ...) for phone number. */ @@ -104,7 +122,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface */ public function isValidPhonenumberAny($phonenumber): bool { - if (false === $this->isPhonenumberValidationConfigured()) { + if (false === $this->isConfigured) { return true; } $validation = $this->performTwilioLookup($phonenumber); @@ -124,7 +142,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface */ public function isValidPhonenumberLandOrVoip($phonenumber): bool { - if (false === $this->isPhonenumberValidationConfigured()) { + if (false === $this->isConfigured) { return true; } @@ -145,7 +163,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface */ public function isValidPhonenumberMobile($phonenumber): bool { - if (false === $this->isPhonenumberValidationConfigured()) { + if (false === $this->isConfigured) { return true; } @@ -160,7 +178,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface private function performTwilioLookup($phonenumber) { - if (false === $this->isPhonenumberValidationConfigured()) { + if (false === $this->isConfigured) { return null; } diff --git a/src/Bundle/ChillMainBundle/Repository/CountryRepository.php b/src/Bundle/ChillMainBundle/Repository/CountryRepository.php index ae318e35e..a7a43f728 100644 --- a/src/Bundle/ChillMainBundle/Repository/CountryRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CountryRepository.php @@ -17,9 +17,9 @@ use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; -final readonly class CountryRepository implements ObjectRepository +class CountryRepository implements ObjectRepository { - private EntityRepository $repository; + private readonly EntityRepository $repository; public function __construct(EntityManagerInterface $entityManager) { diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts b/src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts index 8e9d56695..17db591e1 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/js/date.ts @@ -13,15 +13,15 @@ * */ export const dateToISO = (date: Date | null): string | null => { - if (null === date) { - return null; - } + if (null === date) { + return null; + } - return [ - date.getFullYear(), - (date.getMonth() + 1).toString().padStart(2, "0"), - date.getDate().toString().padStart(2, "0"), - ].join("-"); + return [ + date.getFullYear(), + (date.getMonth() + 1).toString().padStart(2, "0"), + date.getDate().toString().padStart(2, "0"), + ].join("-"); }; /** @@ -30,21 +30,21 @@ export const dateToISO = (date: Date | null): string | null => { * **Experimental** */ export const ISOToDate = (str: string | null): Date | null => { - if (null === str) { - return null; - } - if ("" === str.trim()) { - return null; - } + if (null === str) { + return null; + } + if ("" === str.trim()) { + return null; + } - // If the string already contains time info, use it directly - if (str.includes("T") || str.includes(" ")) { - return new Date(str); - } + // If the string already contains time info, use it directly + if (str.includes("T") || str.includes(" ")) { + return new Date(str); + } - // Otherwise, parse date only - const [year, month, day] = str.split("-").map((p) => parseInt(p)); - return new Date(year, month - 1, day, 0, 0, 0, 0); + // Otherwise, parse date only + const [year, month, day] = str.split("-").map((p) => parseInt(p)); + return new Date(year, month - 1, day, 0, 0, 0, 0); }; /** @@ -52,21 +52,19 @@ export const ISOToDate = (str: string | null): Date | null => { * */ export const ISOToDatetime = (str: string | null): Date | null => { - if (null === str) { - return null; - } + if (null === str) { + return null; + } - const [cal, times] = str.split("T"), - [year, month, date] = cal.split("-").map((s) => parseInt(s)), - [time, timezone] = times.split(times.charAt(8)), - [hours, minutes, seconds] = time.split(":").map((s) => parseInt(s)); - if ("0000" === timezone) { - return new Date( - Date.UTC(year, month - 1, date, hours, minutes, seconds), - ); - } + const [cal, times] = str.split("T"), + [year, month, date] = cal.split("-").map((s) => parseInt(s)), + [time, timezone] = times.split(times.charAt(8)), + [hours, minutes, seconds] = time.split(":").map((s) => parseInt(s)); + if ("0000" === timezone) { + return new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds)); + } - return new Date(year, month - 1, date, hours, minutes, seconds); + return new Date(year, month - 1, date, hours, minutes, seconds); }; /** @@ -74,95 +72,107 @@ export const ISOToDatetime = (str: string | null): Date | null => { * */ export const datetimeToISO = (date: Date): string => { - const cal = [ - date.getFullYear(), - (date.getMonth() + 1).toString().padStart(2, "0"), - date.getDate().toString().padStart(2, "0"), - ].join("-"); + const cal = [ + date.getFullYear(), + (date.getMonth() + 1).toString().padStart(2, "0"), + date.getDate().toString().padStart(2, "0"), + ].join("-"); - const time = [ - date.getHours().toString().padStart(2, "0"), - date.getMinutes().toString().padStart(2, "0"), - date.getSeconds().toString().padStart(2, "0"), - ].join(":"); + const time = [ + date.getHours().toString().padStart(2, "0"), + date.getMinutes().toString().padStart(2, "0"), + date.getSeconds().toString().padStart(2, "0"), + ].join(":"); - const offset = [ - date.getTimezoneOffset() <= 0 ? "+" : "-", - Math.abs(Math.floor(date.getTimezoneOffset() / 60)) - .toString() - .padStart(2, "0"), - ":", - Math.abs(date.getTimezoneOffset() % 60) - .toString() - .padStart(2, "0"), - ].join(""); + const offset = [ + date.getTimezoneOffset() <= 0 ? "+" : "-", + Math.abs(Math.floor(date.getTimezoneOffset() / 60)) + .toString() + .padStart(2, "0"), + ":", + Math.abs(date.getTimezoneOffset() % 60) + .toString() + .padStart(2, "0"), + ].join(""); - const x = cal + "T" + time + offset; + const x = cal + "T" + time + offset; - return x; + return x; }; export const intervalDaysToISO = (days: number | string | null): string => { - if (null === days) { - return "P0D"; - } + if (null === days) { + return "P0D"; + } - return `P${days}D`; + return `P${days}D`; }; export const intervalISOToDays = (str: string | null): number | null => { - if (null === str) { - return null; - } + if (null === str) { + return null; + } - if ("" === str.trim()) { - return null; - } + if ("" === str.trim()) { + return null; + } - let days = 0; - let isDate = true; - let vstring = ""; - for (let i = 0; i < str.length; i = i + 1) { - if (!isDate) { - continue; - } - switch (str.charAt(i)) { - case "P": - isDate = true; - break; - case "T": - isDate = false; - break; - case "0": - case "1": - case "2": - case "3": - case "4": - case "5": - case "6": - case "7": - case "8": - case "9": - vstring = vstring + str.charAt(i); - break; - case "Y": - days = days + Number.parseInt(vstring) * 365; - vstring = ""; - break; - case "M": - days = days + Number.parseInt(vstring) * 30; - vstring = ""; - break; - case "D": - days = days + Number.parseInt(vstring); - vstring = ""; - break; - default: - throw Error( - "this character should not appears: " + str.charAt(i), - ); - } + let days = 0; + let isDate = true; + let vstring = ""; + for (let i = 0; i < str.length; i = i + 1) { + if (!isDate) { + continue; } + switch (str.charAt(i)) { + case "P": + isDate = true; + break; + case "T": + isDate = false; + break; + case "0": + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + vstring = vstring + str.charAt(i); + break; + case "Y": + days = days + Number.parseInt(vstring) * 365; + vstring = ""; + break; + case "M": + days = days + Number.parseInt(vstring) * 30; + vstring = ""; + break; + case "D": + days = days + Number.parseInt(vstring); + vstring = ""; + break; + default: + throw Error("this character should not appears: " + str.charAt(i)); + } + } - return days; + return days; }; + +export function getTimezoneOffsetString(date: Date, timeZone: string): string { + const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" })); + const tzDate = new Date(date.toLocaleString("en-US", { timeZone })); + const offsetMinutes = (utcDate.getTime() - tzDate.getTime()) / (60 * 1000); + + // Inverser le signe pour avoir la convention ±HH:MM + const sign = offsetMinutes <= 0 ? "+" : "-"; + const absMinutes = Math.abs(offsetMinutes); + const hours = String(Math.floor(absMinutes / 60)).padStart(2, "0"); + const minutes = String(absMinutes % 60).padStart(2, "0"); + + return `${sign}${hours}:${minutes}`; +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss index 248b32cfc..f7ccb0d8d 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/buttons.scss @@ -54,7 +54,6 @@ $chill-theme-buttons: ( &.btn-unlink, &.btn-action, &.btn-edit, - &.btn-tpchild, &.btn-wopilink, &.btn-update { &, &:hover { @@ -82,7 +81,6 @@ $chill-theme-buttons: ( &.btn-remove::before, &.btn-choose::before, &.btn-notify::before, - &.btn-tpchild::before, &.btn-download::before, &.btn-search::before, &.btn-cancel::before { @@ -112,7 +110,6 @@ $chill-theme-buttons: ( &.btn-choose::before { content: "\f00c"; } // fa-check // f046 fa-check-square-o &.btn-unlink::before { content: "\f127"; } // fa-chain-broken &.btn-notify::before { content: "\f1d8"; } // fa-paper-plane - &.btn-tpchild::before { content: "\f007"; } // fa-user &.btn-download::before { content: "\f019"; } // fa-download &.btn-search::before { content: "\f002"; } // fa-search } diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss index 28c597bc0..daa22170f 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/forms.scss @@ -29,10 +29,14 @@ form { label { display: inline; - &.required:after { - content: " *"; - color: $red; - } + } +} + +label { + display: inline; + &.required:after { + content: " *"; + color: $red; } } @@ -45,4 +49,4 @@ form { .chill_filter_order { background: $gray-100; -} \ No newline at end of file +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts index 896cbd323..864f6cd52 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/address.ts @@ -1,61 +1,61 @@ import { - Address, - GeographicalUnitLayer, - SimpleGeographicalUnit, + Address, + GeographicalUnitLayer, + SimpleGeographicalUnit, } from "../../types"; import { fetchResults, makeFetch } from "./apiMethods"; export const getAddressById = async (address_id: number): Promise
    => { - const url = `/api/1.0/main/address/${address_id}.json`; + const url = `/api/1.0/main/address/${address_id}.json`; - const response = await fetch(url); + const response = await fetch(url); - if (response.ok) { - return response.json(); - } + if (response.ok) { + return response.json(); + } - throw Error("Error with request resource response"); + throw Error("Error with request resource response"); }; export const getGeographicalUnitsByAddress = async ( - address: Address, + address: Address, ): Promise => { - return fetchResults( - `/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`, - ); + return fetchResults( + `/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`, + ); }; export const getAllGeographicalUnitLayers = async (): Promise< - GeographicalUnitLayer[] + GeographicalUnitLayer[] > => { - return fetchResults( - `/api/1.0/main/geographical-unit-layer.json`, - ); + return fetchResults( + `/api/1.0/main/geographical-unit-layer.json`, + ); }; export const syncAddressWithReference = async ( - address: Address, + address: Address, ): Promise
    => { - return makeFetch( - "POST", - `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`, - ); + return makeFetch( + "POST", + `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`, + ); }; export const markAddressReviewed = async ( - address: Address, + address: Address, ): Promise
    => { - return makeFetch( - "POST", - `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`, - ); + return makeFetch( + "POST", + `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`, + ); }; export const markAddressToReview = async ( - address: Address, + address: Address, ): Promise
    => { - return makeFetch( - "POST", - `/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`, - ); + return makeFetch( + "POST", + `/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`, + ); }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts index 97220f9c8..2083f9260 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/apiMethods.ts @@ -1,285 +1,521 @@ -import { Scope } from "../../types"; +import { + DynamicKeys, + Scope, + ValidationExceptionInterface, + ValidationProblemFromMap, + ViolationFromMap, +} from "../../types"; export type body = Record; export type fetchOption = Record; - +export type Primitive = string | number | boolean | null; export type Params = Record; +export interface Pagination { + first: number; + items_per_page: number; + more: boolean; + next: string | null; + previous: string | null; +} + export interface PaginationResponse { - pagination: { - more: boolean; - items_per_page: number; - }; - results: T[]; - count: number; + pagination: Pagination; + results: T[]; + count: number; } export type FetchParams = Record; export interface TransportExceptionInterface { - name: string; + name: string; } -export interface ValidationExceptionInterface extends TransportExceptionInterface { - name: "ValidationException"; - error: object; - violations: string[]; - titles: string[]; - propertyPaths: string[]; -} +export class ValidationException< + M extends Record> = Record< + string, + Record + >, +> + extends Error + implements ValidationExceptionInterface +{ + public readonly name = "ValidationException" as const; + public readonly problems: ValidationProblemFromMap; + public readonly violations: string[]; + public readonly violationsList: ViolationFromMap[]; + public readonly titles: string[]; + public readonly propertyPaths: DynamicKeys & string[]; + public readonly byProperty: Record, string[]>; -export interface ValidationErrorResponse extends TransportExceptionInterface { - violations: { - title: string; - propertyPath: string; - }[]; -} + constructor(problem: ValidationProblemFromMap) { + const message = [problem.title, problem.detail].filter(Boolean).join(" — "); + super(message); + Object.setPrototypeOf(this, new.target.prototype); -export interface AccessExceptionInterface extends TransportExceptionInterface { - name: "AccessException"; - violations: string[]; -} + this.problems = problem; -export interface NotFoundExceptionInterface extends TransportExceptionInterface { - name: "NotFoundException"; -} + this.violationsList = problem.violations; + this.violations = problem.violations.map( + (v) => `${v.title}: ${v.propertyPath}`, + ); -export interface ServerExceptionInterface extends TransportExceptionInterface { - name: "ServerException"; - message: string; - code: number; - body: string; -} + this.titles = problem.violations.map((v) => v.title); -export interface ConflictHttpExceptionInterface extends TransportExceptionInterface { - name: "ConflictHttpException"; - violations: string[]; + this.propertyPaths = problem.violations.map( + (v) => v.propertyPath, + ) as DynamicKeys & string[]; + + this.byProperty = problem.violations.reduce( + (acc, v) => { + const key = v.propertyPath.replace("/\[\d+\]$/", "") as Extract< + keyof M, + string + >; + (acc[key] ||= []).push(v.title); + return acc; + }, + {} as Record, string[]>, + ); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ValidationException); + } + } + + violationsByNormalizedProperty( + property: Extract, + ): ViolationFromMap[] { + return this.violationsList.filter( + (v) => v.propertyPath.replace(/\[\d+\]$/, "") === property, + ); + } + + violationsByNormalizedPropertyAndParams< + P extends Extract, + K extends Extract, + >(property: P, param: K, param_value: M[P][K]): ViolationFromMap[] { + const list = this.violationsByNormalizedProperty(property); + + return list.filter( + (v): boolean => + !!v.parameters && + // `with_parameter in v.parameters` check indexing + param in v.parameters && + // the cast is safe, because we have overloading that bind the types + (v.parameters as M[P])[param] === param_value, + ); + } } /** - * Generic api method that can be adapted to any fetch request - * - * This method is suitable make a single fetch. When performing a GET to fetch a list of elements, always consider pagination - * and use of the @link{fetchResults} method. + * Check that the exception is a ValidationExceptionInterface + * @param x */ -export const makeFetch = ( - method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE", - url: string, - body?: body | Input | null, - options?: FetchParams, +export function isValidationException< + M extends Record>, +>(x: unknown): x is ValidationExceptionInterface { + return ( + x instanceof ValidationException || + (typeof x === "object" && + x !== null && + (x as any).name === "ValidationException") + ); +} + +export function isValidationProblem(x: unknown): x is { + type: string; + title: string; + violations: { propertyPath: string; title: string }[]; +} { + if (!x || typeof x !== "object") return false; + const o = x as any; + return ( + typeof o.type === "string" && + typeof o.title === "string" && + Array.isArray(o.violations) && + o.violations.every( + (v: any) => + v && + typeof v === "object" && + typeof v.propertyPath === "string" && + typeof v.title === "string", + ) + ); +} + +export interface AccessExceptionInterface extends TransportExceptionInterface { + name: "AccessException"; + violations: string[]; +} + +export interface NotFoundExceptionInterface extends TransportExceptionInterface { + name: "NotFoundException"; +} + +export interface ServerExceptionInterface extends TransportExceptionInterface { + name: "ServerException"; + message: string; + code: number; + body: string; +} + +export interface ConflictHttpExceptionInterface extends TransportExceptionInterface { + name: "ConflictHttpException"; + violations: string[]; +} + +/** + * Generic api method that can be adapted to any fetch request. + * + * What this does + * - Performs a single HTTP request using fetch and returns the parsed JSON as Output. + * - Interprets common API errors and throws typed exceptions you can catch in your UI. + * - When the server returns a Symfony validation problem (HTTP 422), the error is + * rethrown as a typed ValidationException that is aware of your Violation Map (see below). + * + * Important: For GET endpoints that return lists, prefer using fetchResults, which + * handles pagination and aggregation for you. + * + * Violation Map (M): make your 422 errors strongly typed + * ------------------------------------------------------ + * Symfony’s validation problem+json payload looks like this (simplified): + * + * { + * "type": "https://symfony.com/errors/validation", + * "title": "Validation Failed", + * "violations": [ + * { + * "propertyPath": "mobilenumber", + * "title": "This value is not a valid phone number.", + * "parameters": { + * "{{ value }}": "+33 1 02 03 04 05", + * "{{ types }}": "mobile number" + * }, + * "type": "urn:uuid:..." + * } + * ] + * } + * + * The makeFetch generic type parameter M lets you describe, field by field, which + * parameters may appear for each propertyPath. Doing so gives you full type-safety when + * consuming ValidationException in your UI code. + * + * How to build M (Violation Map) + * - M is a map where each key is a server-side propertyPath (string), and the value is a + * record describing the allowed keys in the parameters object for that property. + * - Keys in parameters are the exact strings you receive from Symfony, including the + * curly-braced placeholders such as "{{ value }}", "{{ types }}", etc. + * + * Example from Person creation (WritePersonViolationMap) + * ----------------------------------------------------- + * In ChillPersonBundle/Resources/public/vuejs/_api/OnTheFly.ts you’ll find: + * + * export type WritePersonViolationMap = { + * gender: { + * "{{ value }}": string | null; + * }; + * mobilenumber: { + * "{{ types }}": string; // ex: "mobile number" + * "{{ value }}": string; // ex: "+33 1 02 03 04 05" + * }; + * }; + * + * This means: + * - If the server reports a violation for propertyPath "gender", the parameters object + * is expected to contain a key "{{ value }}" with a string or null value. + * - If the server reports a violation for propertyPath "mobilenumber", the parameters + * may include "{{ value }}" and "{{ types }}" as strings. + * + * How makeFetch uses M + * - When the response has status 422 and the payload matches a Symfony validation + * problem, makeFetch casts it to ValidationProblemFromMap and throws a + * ValidationException. + * - The ValidationException exposes helpful, pre-computed fields: + * - exception.problem: the full typed payload + * - exception.violations: ["Title: propertyPath", ...] + * - exception.titles: ["Title 1", "Title 2", ...] + * - exception.propertyPaths: ["gender", "mobilenumber", ...] (typed from M) + * - exception.byProperty: { gender: [titles...], mobilenumber: [titles...] } + * + * Typical usage patterns + * ---------------------- + * 1) GET without Validation Map (no 422 expected): + * + * const centers = await makeFetch( + * "GET", + * "/api/1.0/person/creation/authorized-centers", + * null + * ); + * + * 2) POST with body and Violation Map: + * + * type WritePersonViolationMap = { + * gender: { "{{ value }}": string | null }; + * mobilenumber: { "{{ types }}": string; "{{ value }}": string }; + * }; + * + * try { + * const created = await makeFetch( + * "POST", + * "/api/1.0/person/person.json", + * personPayload + * ); + * // Success path + * } catch (e) { + * if (isValidationException(e)) { + * // Fully typed: + * e.propertyPaths.includes("mobilenumber"); + * const firstTitleForMobile = e.byProperty.mobilenumber?.[0]; + * // You can also inspect parameter values: + * const v = e.problem.violations.find(v => v.propertyPath === "mobilenumber"); + * const rawValue = v?.parameters?.["{{ value }}"]; // typed as string + * } else { + * // Other error handling (AccessException, ConflictHttpException, etc.) + * } + * } + * + * Tips to design your Violation Map + * - Use exact propertyPath strings as exposed by the API (they usually match your + * DTO field names or entity property paths used by the validator). + * - Inside each property, list only the placeholders that you actually read in the UI + * (you can always add more later). This keeps your types strict but pragmatic. + * - If a field may not include parameters at all, you can set it to an empty object {}. + * - If you don’t care about parameter typing, you can omit M entirely and rely on the + * default loose typing (Record), but you’ll lose safety. + * + * Error taxonomy thrown by makeFetch + * - ValidationException when status = 422 and payload is a validation problem. + * - AccessException when status = 403. + * - ConflictHttpException when status = 409. + * - A generic error object for other non-ok statuses. + * + * @typeParam Input - Shape of the request body you send (if any) + * @typeParam Output - Shape of the successful JSON response you expect + * @typeParam M - Violation Map describing the per-field parameters you expect + * in Symfony validation violations. See examples above. + * + * @param method The HTTP method to use (POST, GET, PUT, PATCH, DELETE) + * @param url The absolute or relative URL to call + * @param body The request payload. If null/undefined, no body is sent + * @param options Extra fetch options/headers merged into the request + * + * @returns The parsed JSON response typed as Output. For 204 No Content, resolves + * with undefined (void). + */ +export const makeFetch = async < + Input, + Output, + M extends Record> = Record< + string, + Record + >, +>( + method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE", + url: string, + body?: body | Input | null, + options?: FetchParams, ): Promise => { - let opts = { - method: method, - headers: { - "Content-Type": "application/json;charset=utf-8", - }, + let opts = { + method: method, + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + }; + + if (body !== null && typeof body !== "undefined") { + Object.assign(opts, { body: JSON.stringify(body) }); + } + + if (typeof options !== "undefined") { + opts = Object.assign(opts, options); + } + + return fetch(url, opts).then(async (response) => { + if (response.status === 204) { + return Promise.resolve(); + } + + if (response.ok) { + return response.json(); + } + + if (response.status === 422) { + // Unprocessable Entity -> payload de validation Symfony + const json = await response.json().catch(() => undefined); + + if (isValidationProblem(json)) { + // On ré-interprète le payload selon M (ParamMap) pour typer les violations + const problem = json as unknown as ValidationProblemFromMap; + throw new ValidationException(problem); + } + + const err = new Error( + "Validation failed but payload is not a ValidationProblem", + ); + (err as any).raw = json; + throw err; + } + + if (response.status === 403) { + throw AccessException(response); + } + + if (response.status === 409) { + throw ConflictHttpException(response); + } + + throw { + name: "Exception", + sta: response.status, + txt: response.statusText, + err: new Error(), + violations: response.body, }; - - if (body !== null && typeof body !== "undefined") { - Object.assign(opts, { body: JSON.stringify(body) }); - } - - if (typeof options !== "undefined") { - opts = Object.assign(opts, options); - } - return fetch(url, opts).then((response) => { - if (response.status === 204) { - return Promise.resolve(); - } - - if (response.ok) { - return response.json(); - } - - if (response.status === 422) { - return response.json().then((response) => { - throw ValidationException(response); - }); - } - - if (response.status === 403) { - throw AccessException(response); - } - - if (response.status === 409) { - throw ConflictHttpException(response); - } - - throw { - name: "Exception", - sta: response.status, - txt: response.statusText, - err: new Error(), - violations: response.body, - }; - }); + }); }; /** * Fetch results with certain parameters */ function _fetchAction( - page: number, - uri: string, - params?: FetchParams, + page: number, + uri: string, + params?: FetchParams, ): Promise> { - const item_per_page = 50; + const item_per_page = 50; - const searchParams = new URLSearchParams(); - searchParams.append("item_per_page", item_per_page.toString()); - searchParams.append("page", page.toString()); + const searchParams = new URLSearchParams(); + searchParams.append("item_per_page", item_per_page.toString()); + searchParams.append("page", page.toString()); - if (params !== undefined) { - Object.keys(params).forEach((key) => { - const v = params[key]; - if (typeof v === "string") { - searchParams.append(key, v); - } else if (typeof v === "number") { - searchParams.append(key, v.toString()); - } else if (v === null) { - searchParams.append(key, ""); - } + if (params !== undefined) { + Object.keys(params).forEach((key) => { + const v = params[key]; + if (typeof v === "string") { + searchParams.append(key, v); + } else if (typeof v === "number") { + searchParams.append(key, v.toString()); + } else if (v === null) { + searchParams.append(key, ""); + } + }); + } + + const url = uri + "?" + searchParams.toString(); + + return fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json;charset=utf-8", + }, + }) + .then((response) => { + if (response.ok) { + return response.json(); + } + + if (response.status === 404) { + throw NotFoundException(response); + } + + if (response.status === 403) { + throw AccessException(response); + } + + if (response.status >= 500) { + return response.text().then((body) => { + throw ServerException(response.status, body); }); - } + } - const url = uri + "?" + searchParams.toString(); - - return fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json;charset=utf-8", - }, + throw new Error("other network error"); }) - .then((response) => { - if (response.ok) { - return response.json(); - } - - if (response.status === 404) { - throw NotFoundException(response); - } - - if (response.status === 422) { - return response.json().then((response) => { - throw ValidationException(response); - }); - } - - if (response.status === 403) { - throw AccessException(response); - } - - if (response.status >= 500) { - return response.text().then((body) => { - throw ServerException(response.status, body); - }); - } - - throw new Error("other network error"); - }) - .catch( - ( - reason: - | NotFoundExceptionInterface - | ServerExceptionInterface - | ValidationExceptionInterface - | TransportExceptionInterface, - ) => { - console.error(reason); - throw reason; - }, - ); + .catch( + ( + reason: + | NotFoundExceptionInterface + | ServerExceptionInterface + | ValidationExceptionInterface + | TransportExceptionInterface, + ) => { + console.error(reason); + throw reason; + }, + ); } export const fetchResults = async ( - uri: string, - params?: FetchParams, + uri: string, + params?: FetchParams, ): Promise => { - const promises: Promise[] = []; - let page = 1; - const firstData: PaginationResponse = (await _fetchAction( - page, - uri, - params, - )) as PaginationResponse; + const promises: Promise[] = []; + let page = 1; + const firstData: PaginationResponse = (await _fetchAction( + page, + uri, + params, + )) as PaginationResponse; - promises.push(Promise.resolve(firstData.results)); + promises.push(Promise.resolve(firstData.results)); - if (firstData.pagination.more) { - do { - page = ++page; - promises.push( - _fetchAction(page, uri, params).then((r) => - Promise.resolve(r.results), - ), - ); - } while (page * firstData.pagination.items_per_page < firstData.count); - } + if (firstData.pagination.more) { + do { + page = ++page; + promises.push( + _fetchAction(page, uri, params).then((r) => + Promise.resolve(r.results), + ), + ); + } while (page * firstData.pagination.items_per_page < firstData.count); + } - return Promise.all(promises).then((values) => values.flat()); + return Promise.all(promises).then((values) => values.flat()); }; export const fetchScopes = (): Promise => { - return fetchResults("/api/1.0/main/scope.json"); -}; - -/** - * Error objects to be thrown - */ -const ValidationException = ( - response: ValidationErrorResponse, -): ValidationExceptionInterface => { - const error = {} as ValidationExceptionInterface; - error.name = "ValidationException"; - error.violations = response.violations.map( - (violation) => `${violation.title}: ${violation.propertyPath}`, - ); - error.titles = response.violations.map((violation) => violation.title); - error.propertyPaths = response.violations.map( - (violation) => violation.propertyPath, - ); - return error; + return fetchResults("/api/1.0/main/scope.json"); }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const AccessException = (response: Response): AccessExceptionInterface => { - const error = {} as AccessExceptionInterface; - error.name = "AccessException"; - error.violations = ["You are not allowed to perform this action"]; + const error = {} as AccessExceptionInterface; + error.name = "AccessException"; + error.violations = ["You are not allowed to perform this action"]; - return error; + return error; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const NotFoundException = (response: Response): NotFoundExceptionInterface => { - const error = {} as NotFoundExceptionInterface; - error.name = "NotFoundException"; + const error = {} as NotFoundExceptionInterface; + error.name = "NotFoundException"; - return error; + return error; }; const ServerException = ( - code: number, - body: string, + code: number, + body: string, ): ServerExceptionInterface => { - const error = {} as ServerExceptionInterface; - error.name = "ServerException"; - error.code = code; - error.body = body; + const error = {} as ServerExceptionInterface; + error.name = "ServerException"; + error.code = code; + error.body = body; - return error; + return error; }; const ConflictHttpException = ( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - response: Response, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + response: Response, ): ConflictHttpExceptionInterface => { - const error = {} as ConflictHttpExceptionInterface; + const error = {} as ConflictHttpExceptionInterface; - error.name = "ConflictHttpException"; - error.violations = [ - "Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again", - ]; + error.name = "ConflictHttpException"; + error.violations = [ + "Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again", + ]; - return error; + return error; }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/export.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/export.ts index 7e350b102..6e66122e7 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/export.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/export.ts @@ -2,17 +2,17 @@ import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; import { ExportGeneration } from "ChillMainAssets/types"; export const fetchExportGenerationStatus = async ( - exportGenerationId: string, + exportGenerationId: string, ): Promise => - makeFetch( - "GET", - `/api/1.0/main/export-generation/${exportGenerationId}/object`, - ); + makeFetch( + "GET", + `/api/1.0/main/export-generation/${exportGenerationId}/object`, + ); export const generateFromSavedExport = async ( - savedExportUuid: string, + savedExportUuid: string, ): Promise => - makeFetch( - "POST", - `/api/1.0/main/export/export-generation/create-from-saved-export/${savedExportUuid}`, - ); + makeFetch( + "POST", + `/api/1.0/main/export/export-generation/create-from-saved-export/${savedExportUuid}`, + ); diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/genderHelper.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/genderHelper.ts new file mode 100644 index 000000000..0e4b38a06 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/genderHelper.ts @@ -0,0 +1,16 @@ +import { Gender, GenderTranslation } from "ChillMainAssets/types"; + +/** + * Translates a given gender object into its corresponding gender translation string. + * + * @param {Gender|null} gender - The gender object to be translated, null values are also supported + * @return {GenderTranslation} Returns the gender translation string corresponding to the provided gender, + * or "unknown" if the gender is null. + */ +export function toGenderTranslation(gender: Gender | null): GenderTranslation { + if (null === gender) { + return "unknown"; + } + + return gender.genderTranslation; +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/locations.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/locations.ts index 3c326ba78..375881924 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/locations.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/locations.ts @@ -2,7 +2,7 @@ import { fetchResults } from "./apiMethods"; import { Location, LocationType } from "../../types"; export const getLocations = (): Promise => - fetchResults("/api/1.0/main/location.json"); + fetchResults("/api/1.0/main/location.json"); export const getLocationTypes = (): Promise => - fetchResults("/api/1.0/main/location-type.json"); + fetchResults("/api/1.0/main/location-type.json"); diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/return_path.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/return_path.ts index e444aa2e5..54a12c030 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/return_path.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/return_path.ts @@ -1,3 +1,3 @@ export function buildReturnPath(location: Location): string { - return location.pathname + location.search; + return location.pathname + location.search; } diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/api/user.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/api/user.ts index 62703b740..e51d09abb 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/api/user.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/api/user.ts @@ -2,23 +2,23 @@ import { User } from "../../types"; import { makeFetch } from "./apiMethods"; export const whoami = (): Promise => { - const url = `/api/1.0/main/whoami.json`; - return fetch(url).then((response) => { - if (response.ok) { - return response.json(); - } - throw { - msg: "Error while getting whoami.", - sta: response.status, - txt: response.statusText, - err: new Error(), - body: response.body, - }; - }); + const url = `/api/1.0/main/whoami.json`; + return fetch(url).then((response) => { + if (response.ok) { + return response.json(); + } + throw { + msg: "Error while getting whoami.", + sta: response.status, + txt: response.statusText, + err: new Error(), + body: response.body, + }; + }); }; export const whereami = (): Promise => { - const url = `/api/1.0/main/user-current-location.json`; + const url = `/api/1.0/main/user-current-location.json`; - return makeFetch("GET", url); + return makeFetch("GET", url); }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/entity-notification/api.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-notification/api.ts index 47cf9b1e9..97ba98d75 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/entity-notification/api.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-notification/api.ts @@ -1,22 +1,22 @@ const buildLinkCreate = function ( - relatedEntityClass: string, - relatedEntityId: number, - to: number | null, - returnPath: string | null, + relatedEntityClass: string, + relatedEntityId: number, + to: number | null, + returnPath: string | null, ): string { - const params = new URLSearchParams(); - params.append("entityClass", relatedEntityClass); - params.append("entityId", relatedEntityId.toString()); + const params = new URLSearchParams(); + params.append("entityClass", relatedEntityClass); + params.append("entityId", relatedEntityId.toString()); - if (null !== to) { - params.append("tos[0]", to.toString()); - } + if (null !== to) { + params.append("tos[0]", to.toString()); + } - if (null !== returnPath) { - params.append("returnPath", returnPath); - } + if (null !== returnPath) { + params.append("returnPath", returnPath); + } - return `/fr/notification/create?${params.toString()}`; + return `/fr/notification/create?${params.toString()}`; }; export { buildLinkCreate }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.ts index a9955f31b..4f9dea715 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/entity-workflow/api.ts @@ -10,17 +10,17 @@ * @throws {Error} If the related entity ID is undefined. */ export const buildLinkCreate = ( - workflowName: string, - relatedEntityClass: string, - relatedEntityId: number | undefined, + workflowName: string, + relatedEntityClass: string, + relatedEntityId: number | undefined, ): string => { - if (typeof relatedEntityId === "undefined") { - throw new Error("the related entity id is not set"); - } - const params = new URLSearchParams(); - params.set("entityClass", relatedEntityClass); - params.set("entityId", relatedEntityId.toString(10)); - params.set("workflow", workflowName); + if (typeof relatedEntityId === "undefined") { + throw new Error("the related entity id is not set"); + } + const params = new URLSearchParams(); + params.set("entityClass", relatedEntityClass); + params.set("entityId", relatedEntityId.toString(10)); + params.set("workflow", workflowName); - return `/fr/main/workflow/create?` + params.toString(); + return `/fr/main/workflow/create?` + params.toString(); }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/localizationHelper/localizationHelper.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/localizationHelper/localizationHelper.ts index 9cd1dcb51..3b67da68e 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/localizationHelper/localizationHelper.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/localizationHelper/localizationHelper.ts @@ -1,4 +1,5 @@ -import { TranslatableString } from "ChillMainAssets/types"; +import { DateTime, TranslatableString } from "ChillMainAssets/types"; +import { getLocale } from "translator"; /** * Localizes a translatable string object based on the current locale. @@ -8,34 +9,97 @@ import { TranslatableString } from "ChillMainAssets/types"; * @param locale defaults to browser locale * @returns The localized string or null if no translation is available */ -export function localizeString( - translatableString: TranslatableString | null | undefined, - locale?: string, -): string { - if (!translatableString || Object.keys(translatableString).length === 0) { - return ""; - } - const currentLocale = locale || navigator.language.split("-")[0] || "fr"; - - if (translatableString[currentLocale]) { - return translatableString[currentLocale]; - } - - // Define fallback locales - const fallbackLocales: string[] = ["fr", "en"]; - - for (const fallbackLocale of fallbackLocales) { - if (translatableString[fallbackLocale]) { - return translatableString[fallbackLocale]; - } - } - - // No fallback translation found, use the first available - const availableLocales = Object.keys(translatableString); - if (availableLocales.length > 0) { - return translatableString[availableLocales[0]]; - } - - return ""; +/** + * Prepends the current HTML lang code to the given URL. + * Example: If lang="fr" and url="/about", returns "/fr/about" + * + * @param url The URL to localize + * @returns The localized URL + */ +export function localizedUrl(url: string): string { + const locale = getLocale(); + // Ensure url starts with a slash and does not already start with /{lang}/ + const normalizedUrl = url.startsWith("/") ? url : `/${url}`; + const langPrefix = `/${locale}`; + if (normalizedUrl.startsWith(langPrefix + "/")) { + return normalizedUrl; + } + return `${langPrefix}${normalizedUrl}`; } + +export function localizeString( + translatableString: TranslatableString | null | undefined, + locale?: string, +): string { + if (!translatableString || Object.keys(translatableString).length === 0) { + return ""; + } + + const currentLocale = locale || getLocale(); + + if (translatableString[currentLocale]) { + return translatableString[currentLocale]; + } + + // Define fallback locales + const fallbackLocales: string[] = ["fr", "en"]; + + for (const fallbackLocale of fallbackLocales) { + if (translatableString[fallbackLocale]) { + return translatableString[fallbackLocale]; + } + } + + // No fallback translation found, use the first available + const availableLocales = Object.keys(translatableString); + if (availableLocales.length > 0) { + return translatableString[availableLocales[0]]; + } + + return ""; +} + +const datetimeFormats: Record< + string, + Record +> = { + fr: { + short: { + year: "numeric", + month: "numeric", + day: "numeric", + }, + text: { + year: "numeric", + month: "long", + day: "numeric", + }, + long: { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "numeric", + minute: "numeric", + hour12: false, + }, + hoursOnly: { + hour: "numeric", + minute: "numeric", + hour12: false, + }, + }, +}; +export function localizeDateTimeFormat( + dateTime: DateTime, + format: keyof typeof datetimeFormats.fr = "short", +): string { + const locale = getLocale(); + const options = + datetimeFormats[locale]?.[format] || datetimeFormats.fr[format]; + return new Intl.DateTimeFormat(locale, options).format( + new Date(dateTime.datetime), + ); +} + +export default datetimeFormats; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/return_path/returnPathHelper.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/return_path/returnPathHelper.ts index e4316329d..468716a14 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/return_path/returnPathHelper.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/return_path/returnPathHelper.ts @@ -6,8 +6,8 @@ * @return {string} The "returnPath" from the query string, or the fallback path if "returnPath" is not present. */ export function returnPathOr(fallbackPath: string): string { - const urlParams = new URLSearchParams(window.location.search); - const returnPath = urlParams.get("returnPath"); + const urlParams = new URLSearchParams(window.location.search); + const returnPath = urlParams.get("returnPath"); - return returnPath ?? fallbackPath; + return returnPath ?? fallbackPath; } diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/api.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/api.ts index 773abbfaf..e75eed4aa 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/api.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/api.ts @@ -2,15 +2,15 @@ import { EntityWorkflow } from "ChillMainAssets/types"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; export const fetchWorkflow = async ( - workflowId: number, + workflowId: number, ): Promise => { - try { - return await makeFetch( - "GET", - `/api/1.0/main/workflow/${workflowId}.json`, - ); - } catch (error) { - console.error(`Failed to fetch workflow ${workflowId}:`, error); - throw error; - } + try { + return await makeFetch( + "GET", + `/api/1.0/main/workflow/${workflowId}.json`, + ); + } catch (error) { + console.error(`Failed to fetch workflow ${workflowId}:`, error); + throw error; + } }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/attachments.ts b/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/attachments.ts index 7e2f23f57..05c745099 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/attachments.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/lib/workflow/attachments.ts @@ -3,20 +3,20 @@ import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/gener import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; export const find_attachments_by_workflow = async ( - workflowId: number, + workflowId: number, ): Promise => - makeFetch("GET", `/api/1.0/main/workflow/${workflowId}/attachment`); + makeFetch("GET", `/api/1.0/main/workflow/${workflowId}/attachment`); export const create_attachment = async ( - workflowId: number, - genericDoc: GenericDocForAccompanyingPeriod, + workflowId: number, + genericDoc: GenericDocForAccompanyingPeriod, ): Promise => - makeFetch("POST", `/api/1.0/main/workflow/${workflowId}/attachment`, { - relatedGenericDocKey: genericDoc.key, - relatedGenericDocIdentifiers: genericDoc.identifiers, - }); + makeFetch("POST", `/api/1.0/main/workflow/${workflowId}/attachment`, { + relatedGenericDocKey: genericDoc.key, + relatedGenericDocIdentifiers: genericDoc.identifiers, + }); export const delete_attachment = async ( - attachment: WorkflowAttachment, + attachment: WorkflowAttachment, ): Promise => - makeFetch("DELETE", `/api/1.0/main/workflow/attachment/${attachment.id}`); + makeFetch("DELETE", `/api/1.0/main/workflow/attachment/${attachment.id}`); diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts b/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts index abe516934..d41a70499 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/address-details/index.ts @@ -7,43 +7,43 @@ import { Address } from "../../types"; const i18n = _createI18n({}); document - .querySelectorAll("span[data-address-details]") - .forEach((el) => { - const dataset = el.dataset as { - addressId: string; - addressRefStatus: string; + .querySelectorAll("span[data-address-details]") + .forEach((el) => { + const dataset = el.dataset as { + addressId: string; + addressRefStatus: string; + }; + + const app = createApp({ + components: { AddressDetailsButton }, + data() { + return { + addressId: Number.parseInt(dataset.addressId), + addressRefStatus: dataset.addressRefStatus, }; - - const app = createApp({ - components: { AddressDetailsButton }, - data() { - return { - addressId: Number.parseInt(dataset.addressId), - addressRefStatus: dataset.addressRefStatus, - }; - }, - template: - '', - methods: { - onUpdateAddress: (address: Address): void => { - if ( - address.refStatus === "to_review" || - address.refStatus === "reviewed" - ) { - // in this two case, the address content do not change - return; - } - if ( - window.confirm( - "L'adresse a été modifiée. Vous pouvez continuer votre travail. Cependant, pour afficher les données immédiatement, veuillez recharger la page. \n\n Voulez-vous recharger la page immédiatement ?", - ) - ) { - window.location.reload(); - } - }, - }, - }); - - app.use(i18n); - app.mount(el); + }, + template: + '', + methods: { + onUpdateAddress: (address: Address): void => { + if ( + address.refStatus === "to_review" || + address.refStatus === "reviewed" + ) { + // in this two case, the address content do not change + return; + } + if ( + window.confirm( + "L'adresse a été modifiée. Vous pouvez continuer votre travail. Cependant, pour afficher les données immédiatement, veuillez recharger la page. \n\n Voulez-vous recharger la page immédiatement ?", + ) + ) { + window.location.reload(); + } + }, + }, }); + + app.use(i18n); + app.mount(el); + }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts b/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts index a396da295..fbbbb5881 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/editor_config.ts @@ -1,16 +1,16 @@ import { - Essentials, - Bold, - Italic, - Paragraph, - Markdown, - BlockQuote, - Heading, - Link, - List, - Emoji, - Mention, - Fullscreen, + Essentials, + Bold, + Italic, + Paragraph, + Markdown, + BlockQuote, + Heading, + Link, + List, + Emoji, + Mention, + Fullscreen, } from "ckeditor5"; import coreTranslations from "ckeditor5/translations/fr.js"; @@ -19,41 +19,41 @@ import "ckeditor5/ckeditor5.css"; import "./index.scss"; export default { - plugins: [ - Essentials, - Markdown, - Bold, - Italic, - BlockQuote, - Heading, - Link, - List, - Paragraph, - // both Emoji and Mention are required for Emoji feature - Emoji, - Mention, - // to enable fullscreen - Fullscreen, + plugins: [ + Essentials, + Markdown, + Bold, + Italic, + BlockQuote, + Heading, + Link, + List, + Paragraph, + // both Emoji and Mention are required for Emoji feature + Emoji, + Mention, + // to enable fullscreen + Fullscreen, + ], + toolbar: { + items: [ + "heading", + "|", + "bold", + "italic", + "link", + "bulletedList", + "numberedList", + "blockQuote", + "|", + "emoji", + "|", + "undo", + "redo", + "|", + "fullscreen", ], - toolbar: { - items: [ - "heading", - "|", - "bold", - "italic", - "link", - "bulletedList", - "numberedList", - "blockQuote", - "|", - "emoji", - "|", - "undo", - "redo", - "|", - "fullscreen", - ], - }, - translations: [coreTranslations], - licenseKey: "GPL", + }, + translations: [coreTranslations], + licenseKey: "GPL", }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts b/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts index 783c0acfa..f8e0cb48b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/ckeditor5/index.ts @@ -2,31 +2,31 @@ import { createApp } from "vue"; import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue"; const ckeditorFields: NodeListOf = - document.querySelectorAll("textarea[ckeditor]"); + document.querySelectorAll("textarea[ckeditor]"); ckeditorFields.forEach((field: HTMLTextAreaElement): void => { - const content = field.value; - const div = document.createElement("div"); + const content = field.value; + const div = document.createElement("div"); - if (field.parentNode !== null) { - field.parentNode.insertBefore(div, field); - } else { - throw "parent is null"; - } + if (field.parentNode !== null) { + field.parentNode.insertBefore(div, field); + } else { + throw "parent is null"; + } - createApp({ - components: { CommentEditor }, - template: ``, - data() { - return { - content, - }; - }, - methods: { - handleInput() { - field.value = this.content; - }, - }, - }).mount(div); + createApp({ + components: { CommentEditor }, + template: ``, + data() { + return { + content, + }; + }, + methods: { + handleInput() { + field.value = this.content; + }, + }, + }).mount(div); - field.style.display = "none"; + field.style.display = "none"; }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts b/src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts index 77ff044ec..1c45f468d 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/collection/index.ts @@ -31,157 +31,157 @@ import "./collection.scss"; declare global { - interface GlobalEventHandlersEventMap { - "show-hide-show": CustomEvent<{ - id: number; - froms: HTMLElement[]; - container: HTMLElement; - }>; - } + interface GlobalEventHandlersEventMap { + "show-hide-show": CustomEvent<{ + id: number; + froms: HTMLElement[]; + container: HTMLElement; + }>; + } } export class CollectionEventPayload { - collection: HTMLUListElement; - entry: HTMLLIElement; + collection: HTMLUListElement; + entry: HTMLLIElement; - constructor(collection: HTMLUListElement, entry: HTMLLIElement) { - this.collection = collection; - this.entry = entry; - } + constructor(collection: HTMLUListElement, entry: HTMLLIElement) { + this.collection = collection; + this.entry = entry; + } } export const handleAdd = (button: any): void => { - const form_name = button.dataset.collectionAddTarget, - prototype = button.dataset.formPrototype, - collection: HTMLUListElement | null = document.querySelector( - 'ul[data-collection-name="' + form_name + '"]', - ); + const form_name = button.dataset.collectionAddTarget, + prototype = button.dataset.formPrototype, + collection: HTMLUListElement | null = document.querySelector( + 'ul[data-collection-name="' + form_name + '"]', + ); - if (collection === null) { - return; + if (collection === null) { + return; + } + + const empty_explain: HTMLLIElement | null = collection.querySelector( + "li[data-collection-empty-explain]", + ), + entry = document.createElement("li"), + counter = collection.querySelectorAll("li.entry").length, // Updated counter logic + content = prototype.replace(/__name__/g, counter.toString()), + event = new CustomEvent("collection-add-entry", { + detail: new CollectionEventPayload(collection, entry), + }); + + console.log(counter); + console.log(content); + + entry.innerHTML = content; + entry.classList.add("entry"); + + if ("collectionRegular" in collection.dataset) { + initializeRemove(collection, entry); + if (empty_explain !== null) { + empty_explain.remove(); } + } - const empty_explain: HTMLLIElement | null = collection.querySelector( - "li[data-collection-empty-explain]", - ), - entry = document.createElement("li"), - counter = collection.querySelectorAll("li.entry").length, // Updated counter logic - content = prototype.replace(/__name__/g, counter.toString()), - event = new CustomEvent("collection-add-entry", { - detail: new CollectionEventPayload(collection, entry), - }); - - console.log(counter); - console.log(content); - - entry.innerHTML = content; - entry.classList.add("entry"); - - if ("collectionRegular" in collection.dataset) { - initializeRemove(collection, entry); - if (empty_explain !== null) { - empty_explain.remove(); - } - } - - collection.appendChild(entry); - collection.dispatchEvent(event); - window.dispatchEvent(event); + collection.appendChild(entry); + collection.dispatchEvent(event); + window.dispatchEvent(event); }; const initializeRemove = ( - collection: HTMLUListElement, - entry: HTMLLIElement, + collection: HTMLUListElement, + entry: HTMLLIElement, ): void => { - const button = buildRemoveButton(collection, entry); - if (null === button) { - return; - } - entry.appendChild(button); + const button = buildRemoveButton(collection, entry); + if (null === button) { + return; + } + entry.appendChild(button); }; export const buildRemoveButton = ( - collection: HTMLUListElement, - entry: HTMLLIElement, + collection: HTMLUListElement, + entry: HTMLLIElement, ): HTMLButtonElement | null => { - const button = document.createElement("button"), - isPersisted = entry.dataset.collectionIsPersisted || "", - content = collection.dataset.collectionButtonRemoveLabel || "", - allowDelete = collection.dataset.collectionAllowDelete || "", - event = new CustomEvent("collection-remove-entry", { - detail: new CollectionEventPayload(collection, entry), - }); - - if (allowDelete === "0" && isPersisted === "1") { - return null; - } - button.classList.add("btn", "btn-delete", "remove-entry"); - button.textContent = content; - button.addEventListener("click", (e: Event) => { - e.preventDefault(); - entry.remove(); - collection.dispatchEvent(event); - window.dispatchEvent(event); + const button = document.createElement("button"), + isPersisted = entry.dataset.collectionIsPersisted || "", + content = collection.dataset.collectionButtonRemoveLabel || "", + allowDelete = collection.dataset.collectionAllowDelete || "", + event = new CustomEvent("collection-remove-entry", { + detail: new CollectionEventPayload(collection, entry), }); - return button; + if (allowDelete === "0" && isPersisted === "1") { + return null; + } + button.classList.add("btn", "btn-delete", "remove-entry"); + button.textContent = content; + button.addEventListener("click", (e: Event) => { + e.preventDefault(); + entry.remove(); + collection.dispatchEvent(event); + window.dispatchEvent(event); + }); + + return button; }; const collectionsInit = new Set(); const buttonsInit = new Set(); const initialize = function (target: Document | Element): void { - const addButtons: NodeListOf = document.querySelectorAll( - "button[data-collection-add-target]", - ), - collections: NodeListOf = document.querySelectorAll( - "ul[data-collection-regular]", - ); + const addButtons: NodeListOf = document.querySelectorAll( + "button[data-collection-add-target]", + ), + collections: NodeListOf = document.querySelectorAll( + "ul[data-collection-regular]", + ); - for (let i = 0; i < addButtons.length; i++) { - const addButton = addButtons[i]; - const uniqid = addButton.dataset.uniqid as string; - if (buttonsInit.has(uniqid)) { - continue; - } - buttonsInit.add(uniqid); - addButton.addEventListener("click", (e: Event) => { - e.preventDefault(); - handleAdd(e.target); - }); + for (let i = 0; i < addButtons.length; i++) { + const addButton = addButtons[i]; + const uniqid = addButton.dataset.uniqid as string; + if (buttonsInit.has(uniqid)) { + continue; } - for (let i = 0; i < collections.length; i++) { - const collection = collections[i]; - const uniqid = collection.dataset.uniqid as string; - if (collectionsInit.has(uniqid)) { - continue; - } - collectionsInit.add(uniqid); - const entries: NodeListOf = - collection.querySelectorAll(":scope > li"); - for (let j = 0; j < entries.length; j++) { - if (entries[j].dataset.collectionEmptyExplain === "1") { - continue; - } - initializeRemove(collections[i], entries[j]); - } + buttonsInit.add(uniqid); + addButton.addEventListener("click", (e: Event) => { + e.preventDefault(); + handleAdd(e.target); + }); + } + for (let i = 0; i < collections.length; i++) { + const collection = collections[i]; + const uniqid = collection.dataset.uniqid as string; + if (collectionsInit.has(uniqid)) { + continue; } + collectionsInit.add(uniqid); + const entries: NodeListOf = + collection.querySelectorAll(":scope > li"); + for (let j = 0; j < entries.length; j++) { + if (entries[j].dataset.collectionEmptyExplain === "1") { + continue; + } + initializeRemove(collections[i], entries[j]); + } + } }; window.addEventListener("DOMContentLoaded", () => { - initialize(document); + initialize(document); }); window.addEventListener( - "show-hide-show", - ( - event: CustomEvent<{ - id: number; - container: HTMLElement; - froms: HTMLElement[]; - }>, - ) => { - const container = event.detail.container as HTMLElement; - initialize(container); - }, + "show-hide-show", + ( + event: CustomEvent<{ + id: number; + container: HTMLElement; + froms: HTMLElement[]; + }>, + ) => { + const container = event.detail.container as HTMLElement; + initialize(container); + }, ); diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read_all.ts b/src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read_all.ts index 97697fca5..d07a7e815 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read_all.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/module/notification/toggle_read_all.ts @@ -5,39 +5,39 @@ import NotificationReadAllToggle from "../../vuejs/_components/Notification/Noti const i18n = _createI18n({}); document.addEventListener("DOMContentLoaded", function () { - const elements = document.querySelectorAll(".notification_all_read"); + const elements = document.querySelectorAll(".notification_all_read"); - elements.forEach((element) => { - console.log("launch"); - createApp({ - template: ``, - components: { - NotificationReadAllToggle, - }, - methods: { - markAsRead(id: number) { - const el = document.querySelector( - `div.notification-status[data-notification-id="${id}"]`, - ); - if (el === null) { - return; - } - el.classList.add("read"); - el.classList.remove("unread"); - }, - markAsUnread(id: number) { - const el = document.querySelector( - `div.notification-status[data-notification-id="${id}"]`, - ); - if (el === null) { - return; - } - el.classList.remove("read"); - el.classList.add("unread"); - }, - }, - }) - .use(i18n) - .mount(element); - }); + elements.forEach((element) => { + console.log("launch"); + createApp({ + template: ``, + components: { + NotificationReadAllToggle, + }, + methods: { + markAsRead(id: number) { + const el = document.querySelector( + `div.notification-status[data-notification-id="${id}"]`, + ); + if (el === null) { + return; + } + el.classList.add("read"); + el.classList.remove("unread"); + }, + markAsUnread(id: number) { + const el = document.querySelector( + `div.notification-status[data-notification-id="${id}"]`, + ); + if (el === null) { + return; + } + el.classList.remove("read"); + el.classList.add("unread"); + }, + }, + }) + .use(i18n) + .mount(element); + }); }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js b/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js index 9f6bb753e..8d9484bb1 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js +++ b/src/Bundle/ChillMainBundle/Resources/public/module/pick-entity/index.js @@ -124,9 +124,10 @@ function loadDynamicPicker(element) { removeEntity({ entity }) { if ( -1 === - this.suggested.findIndex( - (e) => e.type === entity.type && e.id === entity.id, - ) + this.suggested.findIndex( + (e) => e.type === entity.type && e.id === entity.id, + ) && + "me" !== entity ) { this.suggested.push(entity); } diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js deleted file mode 100644 index 7166c08a0..000000000 --- a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import { createApp } from "vue"; -import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n"; -import { appMessages } from "ChillMainAssets/vuejs/HomepageWidget/js/i18n"; -import { store } from "ChillMainAssets/vuejs/HomepageWidget/js/store"; -import App from "ChillMainAssets/vuejs/HomepageWidget/App"; - -const i18n = _createI18n(appMessages); - -const app = createApp({ - template: ``, -}) - .use(store) - .use(i18n) - .component("app", App) - .mount("#homepage_widget"); diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts new file mode 100644 index 000000000..f7dacbefd --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts @@ -0,0 +1,12 @@ +import App from "ChillMainAssets/vuejs/HomepageWidget/App.vue"; +import { createApp } from "vue"; +import { store } from "ChillMainAssets/vuejs/HomepageWidget/store"; + +declare global { + interface Window { + homepage_config: string; + } +} + +const _app = createApp(App); +_app.use(store).mount("#homepage_widget"); diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index c39db4a76..3023ddd98 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -1,296 +1,530 @@ import { - GenericDoc, - isGenericDocWithStoredObject, + GenericDoc, + isGenericDocWithStoredObject, } from "ChillDocStoreAssets/types/generic_doc"; import { StoredObject, StoredObjectStatus } from "ChillDocStoreAssets/types"; +import { CreatableEntityType } from "ChillPersonAssets/types"; +import { ThirdpartyCompany } from "../../../ChillThirdPartyBundle/Resources/public/types"; import { Person } from "../../../ChillPersonBundle/Resources/public/types"; export interface DateTime { - datetime: string; - datetime8601: string; + datetime: string; + datetime8601: string; +} + +/** + * A date representation to use when we create or update a date + */ +export interface DateTimeWrite { + /** + * Must be a string in format Y-m-d\TH:i:sO + */ + datetime: string; } export interface Civility { - id: number; - // TODO + type: "chill_main_civility"; + id: number; + abbreviation: TranslatableString; + active: boolean; + name: TranslatableString; +} + +/** + * Lightweight reference to Civility, to use in POST or PUT requests. + */ +export interface SetCivility { + type: "chill_main_civility"; + id: number; +} + +/** + * Gender translation. + * + * Match the GenderEnum in PHP code. + */ +export type GenderTranslation = "male" | "female" | "neutral" | "unknown"; + +/** + * A gender + * + * See also + */ +export interface Gender { + type: "chill_main_gender"; + id: number; + label: TranslatableString; + genderTranslation: GenderTranslation; +} + +/** + * Lightweight reference to a Gender, used in POST / PUT requests. + */ +export interface SetGender { + type: "chill_main_gender"; + id: number; +} + +export interface Household { + type: "household"; + id: number; } export interface Job { - id: number; - type: "user_job"; - label: { - fr: string; // could have other key. How to do that in ts ? - }; + id: number; + type: "user_job"; + label: { + fr: string; // could have other key. How to do that in ts ? + }; } export interface Center { - id: number; - type: "center"; - name: string; + id: number; + type: "center"; + name: string; + isActive: boolean; +} + +/** + * SetCenter is a lightweight reference used in POST/PUT requests to associate an existing center with a resource. + * It links by id only and does not create or modify centers. + * Expected shape: { type: "center", id: number }. + * Requests will fail if the id is invalid, the center doesn't exist, or permissions are insufficient. + */ +export interface SetCenter { + id: number; + type: "center"; } export interface Scope { - id: number; - type: "scope"; - name: { - fr: string; - }; + id: number; + type: "scope"; + name: { + fr: string; + }; } export interface ResultItem { - result: T; - relevance: number; + result: T; + relevance: number; } export interface User { - type: "user"; - id: number; - username: string; - text: string; - text_without_absence: string; - email: string; - user_job: Job; - label: string; - // todo: mainCenter; mainJob; etc.. + type: "user"; + id: number; + username: string; + text: string; + text_without_absence: string; + email: string; + user_job: Job; + label: string; + // todo: mainCenter; mainJob; etc.. } export interface UserGroup { - type: "user_group"; - id: number; - label: TranslatableString; - backgroundColor: string; - foregroundColor: string; - excludeKey: string; - text: string; + type: "user_group"; + id: number; + label: TranslatableString; + backgroundColor: string; + foregroundColor: string; + excludeKey: string; + text: string; } export type UserGroupOrUser = User | UserGroup; export interface UserAssociatedInterface { - type: "user"; - id: number; + type: "user"; + id: number; } export type TranslatableString = Record; export interface Postcode { - id: number; - name: string; - code: string; - center: Point; + id: number; + name: string; + code: string; + center: Point; } export interface Point { - type: "Point"; - coordinates: [lat: number, lon: number]; + type: "Point"; + coordinates: [lat: number, lon: number]; } export interface Country { - id: number; - name: TranslatableString; - code: string; + id: number; + name: TranslatableString; + code: string; } export type AddressRefStatus = "match" | "to_review" | "reviewed"; export interface Address { - type: "address"; - address_id: number; - text: string; - street: string; - streetNumber: string; - postcode: Postcode; - country: Country; - floor: string | null; - corridor: string | null; - steps: string | null; - flat: string | null; - buildingName: string | null; - distribution: string | null; - extra: string | null; - confidential: boolean; - lines: string[]; - addressReference: AddressReference | null; - validFrom: DateTime; - validTo: DateTime | null; - point: Point | null; - refStatus: AddressRefStatus; - isNoAddress: boolean; + type: "address"; + address_id: number; + text: string; + street: string; + streetNumber: string; + postcode: Postcode; + country: Country; + floor: string | null; + corridor: string | null; + steps: string | null; + flat: string | null; + buildingName: string | null; + distribution: string | null; + extra: string | null; + confidential: boolean; + lines: string[]; + addressReference: AddressReference | null; + validFrom: DateTime; + validTo: DateTime | null; + point: Point | null; + refStatus: AddressRefStatus; + isNoAddress: boolean; +} + +/** + * Associate an existing address in write operations. + */ +export interface SetAddress { + id: number; } export interface AddressWithPoint extends Address { - point: Point; + point: Point; } export interface AddressReference { - id: number; - createdAt: DateTime | null; - deletedAt: DateTime | null; - municipalityCode: string; - point: Point; - postcode: Postcode; - refId: string; - source: string; - street: string; - streetNumber: string; - updatedAt: DateTime | null; + id: number; + createdAt: DateTime | null; + deletedAt: DateTime | null; + municipalityCode: string; + point: Point; + postcode: Postcode; + refId: string; + source: string; + street: string; + streetNumber: string; + updatedAt: DateTime | null; } export interface SimpleGeographicalUnit { - id: number; - layerId: number; - unitName: string; - unitRefId: string; + id: number; + layerId: number; + unitName: string; + unitRefId: string; } export interface GeographicalUnitLayer { - id: number; - name: TranslatableString; - refId: string; + id: number; + name: TranslatableString; + refId: string; } export interface Location { - type: "location"; - id: number; - active: boolean; - address: Address | null; - availableForUsers: boolean; - createdAt: DateTime | null; - createdBy: User | null; - updatedAt: DateTime | null; - updatedBy: User | null; - email: string | null; - name: string; - phonenumber1: string | null; - phonenumber2: string | null; - locationType: LocationType; + type: "location"; + id: number; + active: boolean; + address: Address | null; + availableForUsers: boolean; + createdAt: DateTime | null; + createdBy: User | null; + updatedAt: DateTime | null; + updatedBy: User | null; + email: string | null; + name: string; + phonenumber1: string | null; + phonenumber2: string | null; + locationType: LocationType; } export interface LocationAssociated { - type: "location"; - id: number; + type: "location"; + id: number; } export interface LocationType { - type: "location-type"; - id: number; - active: boolean; - addressRequired: "optional" | "required"; - availableForUsers: boolean; - editableByUsers: boolean; - contactData: "optional" | "required"; - title: TranslatableString; + type: "location-type"; + id: number; + active: boolean; + addressRequired: "optional" | "required"; + availableForUsers: boolean; + editableByUsers: boolean; + contactData: "optional" | "required"; + title: TranslatableString; } export interface NewsItemType { - id: number; - title: string; - content: string; - startDate: DateTime; - endDate: DateTime | null; + id: number; + title: string; + content: string; + startDate: DateTime; + endDate: DateTime | null; } export interface WorkflowAvailable { - name: string; - text: string; + name: string; + text: string; } export interface WorkflowAttachment { - id: number; - relatedGenericDocKey: string; - relatedGenericDocIdentifiers: object; - createdAt: DateTime | null; - createdBy: User | null; - updatedAt: DateTime | null; - updatedBy: User | null; - genericDoc: null | GenericDoc; + id: number; + relatedGenericDocKey: string; + relatedGenericDocIdentifiers: object; + createdAt: DateTime | null; + createdBy: User | null; + updatedAt: DateTime | null; + updatedBy: User | null; + genericDoc: null | GenericDoc; } export type AttachmentWithDocAndStored = WorkflowAttachment & { - genericDoc: GenericDoc & { storedObject: StoredObject }; + genericDoc: GenericDoc & { storedObject: StoredObject }; }; export function isAttachmentWithDocAndStored( - a: WorkflowAttachment, + a: WorkflowAttachment, ): a is AttachmentWithDocAndStored { - return ( - isWorkflowAttachmentWithGenericDoc(a) && - isGenericDocWithStoredObject(a.genericDoc) - ); + return ( + isWorkflowAttachmentWithGenericDoc(a) && + isGenericDocWithStoredObject(a.genericDoc) + ); } export function isWorkflowAttachmentWithGenericDoc( - attachment: WorkflowAttachment, + attachment: WorkflowAttachment, ): attachment is WorkflowAttachment & { genericDoc: GenericDoc } { - return attachment.genericDoc !== null; + return attachment.genericDoc !== null; } export interface Workflow { - name: string; - text: string; + name: string; + text: string; } export interface EntityWorkflowStep { - type: "entity_workflow_step"; - id: number; - comment: string; - currentStep: StepDefinition; - isFinal: boolean; - isFreezed: boolean; - isFinalized: boolean; - transitionPrevious: Transition | null; - transitionAfter: Transition | null; - previousId: number | null; - nextId: number | null; - transitionPreviousBy: User | null; - transitionPreviousAt: DateTime | null; + type: "entity_workflow_step"; + id: number; + comment: string; + currentStep: StepDefinition; + isFinal: boolean; + isFreezed: boolean; + isFinalized: boolean; + transitionPrevious: Transition | null; + transitionAfter: Transition | null; + previousId: number | null; + nextId: number | null; + transitionPreviousBy: User | null; + transitionPreviousAt: DateTime | null; } export interface Transition { - name: string; - text: string; - isForward: boolean; + name: string; + text: string; + isForward: boolean; } export interface StepDefinition { - name: string; - text: string; + name: string; + text: string; } export interface EntityWorkflow { - type: "entity_workflow"; - id: number; - relatedEntityClass: string; - relatedEntityId: number; - workflow: Workflow; - currentStep: EntityWorkflowStep; - steps: EntityWorkflowStep[]; - datas: WorkflowData; - title: string; - isOnHoldAtCurrentStep: boolean; - _permissions: { - CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT: boolean; - }; + type: "entity_workflow"; + id: number; + relatedEntityClass: string; + relatedEntityId: number; + workflow: Workflow; + currentStep: EntityWorkflowStep; + steps: EntityWorkflowStep[]; + datas: WorkflowData; + title: string; + isOnHoldAtCurrentStep: boolean; + _permissions: { + CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT: boolean; + }; } export interface WorkflowData { - persons: Person[]; + persons: Person[]; } export interface ExportGeneration { - id: string; - type: "export_generation"; - exportAlias: string; - createdBy: User | null; - createdAt: DateTime | null; - status: StoredObjectStatus; - storedObject: StoredObject; + id: string; + type: "export_generation"; + exportAlias: string; + createdBy: User | null; + createdAt: DateTime | null; + status: StoredObjectStatus; + storedObject: StoredObject; } export interface PrivateCommentEmbeddable { - comments: Record; + comments: Record; } +// API Exception types +export interface TransportExceptionInterface { + name: string; +} + +type IndexedKey = `${Base}[${number}]`; +type BaseKeys = Extract; + +export type DynamicKeys>> = + | BaseKeys + | { [K in BaseKeys as IndexedKey]: K }[IndexedKey>]; + +type NormalizeKey = K extends `${infer B}[${number}]` ? B : K; + +export type ViolationFromMap< + M extends Record>, +> = { + [K in DynamicKeys & string]: { + // <- note le "& string" ici + propertyPath: K; + title: string; + parameters?: M[NormalizeKey]; + type?: string; + }; +}[DynamicKeys & string]; + +export type ValidationProblemFromMap< + M extends Record>, +> = { + type: string; + title: string; + detail?: string; + violations: ViolationFromMap[]; +} & Record; + +export interface ValidationExceptionInterface< + M extends Record> = Record< + string, + Record + >, +> extends Error { + name: "ValidationException"; + /** Full server payload copy */ + problems: ValidationProblemFromMap; + /** A list of all violations, with property key */ + violationsList: ViolationFromMap[]; + /** Compact list "Title: path" */ + violations: string[]; + /** Only titles */ + titles: string[]; + /** Only property paths */ + propertyPaths: DynamicKeys & string[]; + /** Indexing by property (useful for display by field) */ + byProperty: Record, string[]>; + + violationsByNormalizedProperty( + property: Extract, + ): ViolationFromMap[]; + + violationsByNormalizedPropertyAndParams< + P extends Extract, + K extends Extract, + >( + property: P, + param: K, + param_value: M[P][K], + ): ViolationFromMap[]; +} + +export interface AccessExceptionInterface extends TransportExceptionInterface { + name: "AccessException"; + violations: string[]; +} + +export interface NotFoundExceptionInterface extends TransportExceptionInterface { + name: "NotFoundException"; +} + +export interface ServerExceptionInterface extends TransportExceptionInterface { + name: "ServerException"; + message: string; + code: number; + body: string; +} + +export interface ConflictHttpExceptionInterface extends TransportExceptionInterface { + name: "ConflictHttpException"; + violations: string[]; +} + +export type ApiException = + | ValidationExceptionInterface + | AccessExceptionInterface + | NotFoundExceptionInterface + | ServerExceptionInterface + | ConflictHttpExceptionInterface; + +export interface Modal { + showModal: boolean; + modalDialogClass: string; +} + +export interface Selected { + result: UserGroupOrUser; +} + +export interface addNewEntities { + selected: Selected[]; + modal: Modal; +} + +export enum HomepageTabs { + MyCustoms, + MyTickets, + MyNotifications, + MyAccompanyingCourses, + MyEvaluations, + MyTasks, + MyWorkflows, +} + +/** + * The configuration for homepage config. + * + * This config comes from configuration (see ChillMainBundle/DependencyInjection/Configuration or chill_main.homepage configuration + * in packages/config files). It goes through a twig globals, and is displayed in the homepage. + */ +export interface HomepageConfig { + default_tab: HomepageTabs; + display_tabs: HomepageTabs[]; +} + +export interface TabDefinition { + key: HomepageTabs; + label: string; + icon: string | null; + counter: () => number; +} + +export interface CreateComponentConfigGeneral { + action: "create"; + allowedTypes: CreatableEntityType[]; + query: string; + parent: null; +} + +export interface CreateComponentThirdPartyAddContact { + action: "addContact"; + allowedTypes: readonly ["thirdparty"]; + query: string; + parent: ThirdpartyCompany; +} + +/** + * Configuration for the CreateModal and Create component + */ +export type CreateComponentConfig = + | CreateComponentConfigGeneral + | CreateComponentThirdPartyAddContact; + /** * Possible states for the WaitingScreen Component. */ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue index 075bf6d65..d259d0cae 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue @@ -1,130 +1,130 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ActionButtons.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ActionButtons.vue index 5d90bf818..fda84c817 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ActionButtons.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ActionButtons.vue @@ -1,32 +1,32 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue index ffe7f0bc1..0b3a42e69 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress.vue @@ -1,264 +1,255 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMap.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMap.vue index 167b3b981..4aa44f4a6 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMap.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMap.vue @@ -1,5 +1,5 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue index 9a6ffda10..bfa10fe3a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressMore.vue @@ -1,181 +1,177 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue index c1c644946..df10b1468 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/AddressSelection.vue @@ -1,251 +1,245 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue index 02e24a1e6..459b20831 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CitySelection.vue @@ -1,273 +1,268 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue index dfd3ecb60..886eab581 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/AddAddress/CountrySelection.vue @@ -1,105 +1,101 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue index 96e67f952..3e8e1f4bb 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/DatePane.vue @@ -1,69 +1,66 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue index f636d0b27..0e6ef77c5 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/EditPane.vue @@ -1,104 +1,101 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ShowPane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ShowPane.vue index ebeeda7f2..035341180 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ShowPane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/ShowPane.vue @@ -1,240 +1,227 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue index ddaed6219..2e62b5963 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/components/SuggestPane.vue @@ -1,67 +1,64 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/App.vue index b9ffeff34..8cd9397f9 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/App.vue @@ -1,10 +1,45 @@ + + - - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/index.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/index.ts index e970e51b9..ae77fbb08 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/DownloadExport/index.ts @@ -4,8 +4,8 @@ import App from "./App.vue"; const el = document.getElementById("app"); if (null === el) { - console.error("div element app was not found"); - throw new Error("div element app was not found"); + console.error("div element app was not found"); + throw new Error("div element app was not found"); } const exportGenerationId = el?.dataset.exportGenerationId as string; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue index 8686b59a7..3ec07682b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue @@ -1,159 +1,157 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue index 5633d3133..4f664d6a5 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/News.vue @@ -1,13 +1,13 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue index 997eae58b..f98451249 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/DashboardWidgets/NewsItem.vue @@ -1,40 +1,42 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue index 9f7edf8e7..bc69302ac 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyAccompanyingCourses.vue @@ -1,112 +1,119 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue index 1f76a5a0a..2bb221e25 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyCustoms.vue @@ -1,163 +1,150 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue index 71bf654e7..5d9187b0a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyEvaluations.vue @@ -1,138 +1,162 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue index 377903c01..81746cbab 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyNotifications.vue @@ -1,117 +1,143 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue index ca30197a8..aa929b086 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTasks.vue @@ -1,123 +1,140 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTickets.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTickets.vue new file mode 100644 index 000000000..50bc99f6c --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyTickets.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue index 68ac0bc1e..5fc62f444 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflows.vue @@ -1,26 +1,34 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflowsTable.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflowsTable.vue index 61dfa420d..1b36ba1c5 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflowsTable.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorkflowsTable.vue @@ -1,98 +1,106 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue index 8f7dea7ef..9cccd6dd6 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/MyWorks.vue @@ -1,122 +1,120 @@ -// CURRENTLY NOT IN USE - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue index daec6db4a..712a5247c 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue @@ -1,17 +1,17 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue index c8899aa86..0ca7ed273 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabTable.vue @@ -1,21 +1,18 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js deleted file mode 100644 index 574854c91..000000000 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/i18n.js +++ /dev/null @@ -1,89 +0,0 @@ -const appMessages = { - fr: { - main_title: "Vue d'ensemble", - my_works: { - tab: "Mes actions", - description: - "Liste des actions d'accompagnement dont je suis référent et qui arrivent à échéance.", - }, - my_evaluations: { - tab: "Mes évaluations", - description: - "Liste des évaluations dont je suis référent et qui arrivent à échéance.", - }, - my_tasks: { - tab: "Mes tâches", - description_alert: - "Liste des tâches auxquelles je suis assigné et dont la date de rappel est dépassée.", - description_warning: - "Liste des tâches auxquelles je suis assigné et dont la date d'échéance est dépassée.", - }, - my_accompanying_courses: { - tab: "Mes nouveaux parcours", - description: - "Liste des parcours d'accompagnement que l'on vient de m'attribuer depuis moins de 15 jours.", - }, - my_notifications: { - tab: "Mes nouvelles notifications", - description: "Liste des notifications reçues et non lues.", - }, - my_workflows: { - tab: "Mes workflows", - description: "Liste des workflows en attente d'une action.", - description_cc: "Liste des workflows dont je suis en copie.", - }, - opening_date: "Date d'ouverture", - social_issues: "Problématiques sociales", - concerned_persons: "Usagers concernés", - max_date: "Date d'échéance", - warning_date: "Date de rappel", - evaluation: "Évaluation", - task: "Tâche", - Date: "Date", - From: "Expéditeur", - Subject: "Objet", - Entity: "Associé à", - Step: "Étape", - concerned_users: "Usagers concernés", - Object_workflow: "Objet du workflow", - on_hold: "En attente", - show_entity: "Voir {entity}", - the_activity: "l'échange", - the_course: "le parcours", - the_action: "l'action", - the_evaluation: "l'évaluation", - the_evaluation_document: "le document", - the_task: "la tâche", - the_workflow: "le workflow", - StartDate: "Date d'ouverture", - SocialAction: "Action d'accompagnement", - no_data: "Aucun résultats", - no_dashboard: "Pas de tableaux de bord", - counter: { - unread_notifications: - "{n} notification non lue | {n} notifications non lues", - assignated_courses: - "{n} parcours récent assigné | {n} parcours récents assignés", - assignated_actions: "{n} action assignée | {n} actions assignées", - assignated_evaluations: - "{n} évaluation assignée | {n} évaluations assignées", - alert_tasks: "{n} tâche en rappel | {n} tâches en rappel", - warning_tasks: "{n} tâche à échéance | {n} tâches à échéance", - }, - emergency: "Urgent", - confidential: "Confidentiel", - automatic_notification: "Notification automatique", - widget: { - news: { - title: "Actualités", - readMore: "Lire la suite", - date: "Date", - none: "Aucune actualité", - }, - }, - }, -}; - -Object.assign(appMessages.fr); - -export { appMessages }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/index.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/index.ts new file mode 100644 index 000000000..e21c6929a --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/index.ts @@ -0,0 +1,55 @@ +import { createStore } from "vuex"; +import { State as HomepageStates, moduleHomepage } from "./modules/homepage"; + +import { + State as TicketListStates, + moduleTicketList, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list"; +import { + State as TicketStates, + moduleTicket, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket"; +import { + State as CommentStates, + moduleComment, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/comment"; +import { + moduleMotive, + State as MotiveStates, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/motive"; +import { + State as AddresseeStates, + moduleAddressee, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/addressee"; +import { + modulePersons, + State as PersonsState, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/persons"; + +import { + moduleUser, + State as UserState, +} from "../../../../../../ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/user"; + +export interface RootState { + homepage: HomepageStates; + motive: MotiveStates; + ticket: TicketStates; + comment: CommentStates; + addressee: AddresseeStates; + persons: PersonsState; + ticketList: TicketListStates; + user: UserState; +} +export const store = createStore({ + modules: { + homepage: moduleHomepage, + motive: moduleMotive, + ticket: moduleTicket, + comment: moduleComment, + addressee: moduleAddressee, + person: modulePersons, + ticketList: moduleTicketList, + user: moduleUser, + }, +}); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts similarity index 64% rename from src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js rename to src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts index ed88b857d..4fc8dae8c 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/js/store.js +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts @@ -1,90 +1,104 @@ import "es6-promise/auto"; -import { createStore } from "vuex"; -import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; +import { Module } from "vuex"; +import { + makeFetch, + PaginationResponse, +} from "ChillMainAssets/lib/api/apiMethods"; +import { + AccompanyingCourse, + Alert, + Evaluation, + Warning, + Workflow, + WorflowCc, + Notification, +} from "ChillPersonAssets/types"; +import { RootState } from ".."; +import { HomepageTabs } from "ChillMainAssets/types"; -const debug = process.env.NODE_ENV !== "production"; +export interface TasksState { + warning: PaginationResponse; + alert: PaginationResponse; +} -const isEmpty = (obj) => { - return ( - obj && - Object.keys(obj).length <= 1 && - Object.getPrototypeOf(obj) === Object.prototype - ); -}; +export interface State { + evaluations: PaginationResponse; + tasks: TasksState; + accompanyingCourses: PaginationResponse; + notifications: PaginationResponse; + workflows: PaginationResponse; + workflowsCc: PaginationResponse; + errorMsg: unknown[]; + loading: boolean; + ticketsLoading: boolean; +} -const store = createStore({ - strict: debug, +export const moduleHomepage: Module = { state: { - // works: {}, - evaluations: {}, + evaluations: { + count: 0, + } as PaginationResponse, tasks: { - warning: {}, - alert: {}, + warning: { + count: 0, + } as PaginationResponse, + alert: { + count: 0, + } as PaginationResponse, }, - accompanyingCourses: {}, - notifications: {}, - workflows: {}, - workflowsCc: {}, + accompanyingCourses: { + count: 0, + } as PaginationResponse, + notifications: { + count: 0, + } as PaginationResponse, + workflows: { + count: 0, + } as PaginationResponse, + workflowsCc: { + count: 0, + } as PaginationResponse, + ticketsLoading: false, errorMsg: [], loading: false, }, getters: { - // isWorksLoaded(state) { - // return !isEmpty(state.works); - // }, + isTicketLoading: (state) => { + return state.ticketsLoading; + }, isEvaluationsLoaded(state) { - return !isEmpty(state.evaluations); + return Array.isArray(state.evaluations.results); }, isTasksWarningLoaded(state) { - return !isEmpty(state.tasks.warning); + return Array.isArray(state.tasks.warning.results); }, isTasksAlertLoaded(state) { - return !isEmpty(state.tasks.alert); + return Array.isArray(state.tasks.alert.results); }, isAccompanyingCoursesLoaded(state) { - return !isEmpty(state.accompanyingCourses); + return Array.isArray(state.accompanyingCourses.results); }, isNotificationsLoaded(state) { - return !isEmpty(state.notifications); + return Array.isArray(state.notifications.results); }, isWorkflowsLoaded(state) { - return !isEmpty(state.workflows); - }, - counter(state) { - return { - // works: state.works.count, - evaluations: state.evaluations.count, - tasksWarning: state.tasks.warning.count, - tasksAlert: state.tasks.alert.count, - accompanyingCourses: state.accompanyingCourses.count, - notifications: state.notifications.count, - workflows: state.workflows.count, - }; + return Array.isArray(state.workflows.results); }, }, mutations: { - // addWorks(state, works) { - // //console.log('addWorks', works); - // state.works = works; - // }, addEvaluations(state, evaluations) { - //console.log('addEvaluations', evaluations); state.evaluations = evaluations; }, addTasksWarning(state, tasks) { - //console.log('addTasksWarning', tasks); state.tasks.warning = tasks; }, addTasksAlert(state, tasks) { - //console.log('addTasksAlert', tasks); state.tasks.alert = tasks; }, addCourses(state, courses) { - //console.log('addCourses', courses); state.accompanyingCourses = courses; }, addNotifications(state, notifications) { - //console.log('addNotifications', notifications); state.notifications = notifications; }, addWorkflows(state, workflows) { @@ -96,30 +110,30 @@ const store = createStore({ setLoading(state, bool) { state.loading = bool; }, + setTicketsLoading(state, bool) { + state.ticketsLoading = bool; + }, catchError(state, error) { state.errorMsg.push(error); }, }, actions: { - getByTab({ commit, getters }, { tab, param }) { + async getByTab({ commit, getters, dispatch }, { tab, param }) { switch (tab) { - // case 'MyWorks': - // if (!getters.isWorksLoaded) { - // commit('setLoading', true); - // const url = `/api/1.0/person/accompanying-period/work/my-near-end${'?'+ param}`; - // makeFetch('GET', url) - // .then((response) => { - // commit('addWorks', response); - // commit('setLoading', false); - // }) - // .catch((error) => { - // commit('catchError', error); - // throw error; - // }) - // ; - // } - // break; - case "MyEvaluations": + case HomepageTabs.MyTickets: + if (!getters.isTicketsLoaded) { + commit("setTicketsLoading", true); + // Utilise l'action du module ticket_list + await dispatch( + "fetchTicketList", + { byAddresseeToMe: true, byCurrentState: "open" }, + { root: true }, + ); + + commit("setTicketsLoading", false); + } + break; + case HomepageTabs.MyEvaluations: if (!getters.isEvaluationsLoaded) { commit("setLoading", true); const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${"?" + param}`; @@ -134,7 +148,7 @@ const store = createStore({ }); } break; - case "MyTasks": + case HomepageTabs.MyTasks: if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) { commit("setLoading", true); const urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${"&" + param}`, @@ -159,7 +173,7 @@ const store = createStore({ }); } break; - case "MyAccompanyingCourses": + case HomepageTabs.MyAccompanyingCourses: if (!getters.isAccompanyingCoursesLoaded) { commit("setLoading", true); const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${"?" + param}`; @@ -174,13 +188,13 @@ const store = createStore({ }); } break; - case "MyNotifications": + case HomepageTabs.MyNotifications: if (!getters.isNotificationsLoaded) { commit("setLoading", true); const url = `/api/1.0/main/notification/my/unread${"?" + param}`; + makeFetch("GET", url) .then((response) => { - console.log("notifications", response); commit("addNotifications", response); commit("setLoading", false); }) @@ -190,7 +204,7 @@ const store = createStore({ }); } break; - case "MyWorkflows": + case HomepageTabs.MyWorkflows: if (!getters.isWorflowsLoaded) { commit("setLoading", true); makeFetch("GET", "/api/1.0/main/workflow/my") @@ -217,6 +231,4 @@ const store = createStore({ } }, }, -}); - -export { store }; +}; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/App.vue index 24c5421a8..ca60c23c7 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/App.vue @@ -1,42 +1,42 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/Create.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/Create.vue index 9a4325060..d4899fa42 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/Create.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/Create.vue @@ -1,113 +1,140 @@ + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/CreateModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/CreateModal.vue new file mode 100644 index 000000000..0357b8a57 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/CreateModal.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue index 452ac0396..44e90122b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/OnTheFly/components/OnTheFly.vue @@ -1,328 +1,361 @@ - - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickPostalCode/PickPostalCode.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickPostalCode/PickPostalCode.vue index 191bafc76..fe5fcbe17 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickPostalCode/PickPostalCode.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/PickPostalCode/PickPostalCode.vue @@ -1,27 +1,27 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/App.vue index 6f021ac80..bef3028dc 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/App.vue @@ -2,17 +2,15 @@ import GenerateButton from "ChillMainAssets/vuejs/SavedExportButtons/Component/GenerateButton.vue"; interface SavedExportButtonsConfig { - savedExportUuid: string; - savedExportAlias: string; + savedExportUuid: string; + savedExportAlias: string; } const props = defineProps(); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/Component/GenerateButton.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/Component/GenerateButton.vue index f4dafd006..0d1ad9703 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/Component/GenerateButton.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/Component/GenerateButton.vue @@ -1,15 +1,15 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/index.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/index.ts index 518746dea..a7d0105e8 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/SavedExportButtons/index.ts @@ -3,11 +3,11 @@ import { createApp } from "vue"; import App from "./App.vue"; const buttons = document.querySelectorAll( - "[data-generate-export-button]", + "[data-generate-export-button]", ); buttons.forEach((button) => { - const savedExportUuid = button.dataset.savedExportUuid as string; + const savedExportUuid = button.dataset.savedExportUuid as string; - createApp(App, { savedExportUuid, savedExportAlias: "" }).mount(button); + createApp(App, { savedExportUuid, savedExportAlias: "" }).mount(button); }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/App.vue index 578cb6558..88401213b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/App.vue @@ -6,15 +6,15 @@ import { ref } from "vue"; import WaitingScreen from "ChillMainAssets/vuejs/_components/WaitingScreen.vue"; import { WaitingScreenState } from "ChillMainAssets/types"; import { - trans, - WORKFLOW_WAIT_TITLE, - WORKFLOW_WAIT_ERROR_WHILE_WAITING, - WORKFLOW_WAIT_SUCCESS, + trans, + WORKFLOW_WAIT_TITLE, + WORKFLOW_WAIT_ERROR_WHILE_WAITING, + WORKFLOW_WAIT_SUCCESS, } from "translator"; interface WaitPostProcessWorkflowComponentProps { - workflowId: number; - expectedStep: string; + workflowId: number; + expectedStep: string; } const props = defineProps(); @@ -24,52 +24,52 @@ const MAX_TRYIES = 50; const state = ref("pending"); const { pause, resume } = useIntervalFn( - async () => { - try { - const workflow = await fetchWorkflow(props.workflowId); - counter.value++; - if (workflow.currentStep.currentStep.name === props.expectedStep) { - window.location.assign( - returnPathOr("/fr/main/workflow" + workflow.id + "/show"), - ); - resume(); - state.value = "ready"; - } + async () => { + try { + const workflow = await fetchWorkflow(props.workflowId); + counter.value++; + if (workflow.currentStep.currentStep.name === props.expectedStep) { + window.location.assign( + returnPathOr("/fr/main/workflow" + workflow.id + "/show"), + ); + resume(); + state.value = "ready"; + } - if (counter.value > MAX_TRYIES) { - pause(); - state.value = "failure"; - } - } catch (error) { - console.error(error); - pause(); - } - }, - 2000, - { immediate: true }, + if (counter.value > MAX_TRYIES) { + pause(); + state.value = "failure"; + } + } catch (error) { + console.error(error); + pause(); + } + }, + 2000, + { immediate: true }, ); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/index.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/index.ts index ac52a90cd..55b0a4331 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WaitPostProcessWorkflow/index.ts @@ -2,50 +2,50 @@ import { createApp } from "vue"; import App from "./App.vue"; function mountApp(): void { - const el = document.querySelector(".screen-wait"); - if (!el) { - console.error( - "WaitPostProcessWorkflow: mount element .screen-wait not found", - ); - return; - } + const el = document.querySelector(".screen-wait"); + if (!el) { + console.error( + "WaitPostProcessWorkflow: mount element .screen-wait not found", + ); + return; + } - const workflowIdAttr = el.getAttribute("data-workflow-id"); - const expectedStep = el.getAttribute("data-expected-step") || ""; + const workflowIdAttr = el.getAttribute("data-workflow-id"); + const expectedStep = el.getAttribute("data-expected-step") || ""; - if (!workflowIdAttr) { - console.error( - "WaitPostProcessWorkflow: data-workflow-id attribute missing on mount element", - ); - return; - } + if (!workflowIdAttr) { + console.error( + "WaitPostProcessWorkflow: data-workflow-id attribute missing on mount element", + ); + return; + } - if (!expectedStep) { - console.error( - "WaitPostProcessWorkflow: data-expected-step attribute missing on mount element", - ); - return; - } + if (!expectedStep) { + console.error( + "WaitPostProcessWorkflow: data-expected-step attribute missing on mount element", + ); + return; + } - const workflowId = Number(workflowIdAttr); - if (Number.isNaN(workflowId)) { - console.error( - "WaitPostProcessWorkflow: data-workflow-id is not a valid number:", - workflowIdAttr, - ); - return; - } + const workflowId = Number(workflowIdAttr); + if (Number.isNaN(workflowId)) { + console.error( + "WaitPostProcessWorkflow: data-workflow-id is not a valid number:", + workflowIdAttr, + ); + return; + } - const app = createApp(App, { - workflowId, - expectedStep, - }); + const app = createApp(App, { + workflowId, + expectedStep, + }); - app.mount(el); + app.mount(el); } if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", mountApp); + document.addEventListener("DOMContentLoaded", mountApp); } else { - mountApp(); + mountApp(); } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/App.vue index af319580d..a9a201275 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/App.vue @@ -9,17 +9,17 @@ import { fetchWorkflow } from "ChillMainAssets/lib/workflow/api"; import { trans, WORKFLOW_ATTACHMENTS_ADD_AN_ATTACHMENT } from "translator"; interface AppConfig { - workflowId: number; - accompanyingPeriodId: number; - attachments: WorkflowAttachment[]; + workflowId: number; + accompanyingPeriodId: number; + attachments: WorkflowAttachment[]; } const emit = defineEmits<{ - ( - e: "pickGenericDoc", - payload: { genericDoc: GenericDocForAccompanyingPeriod }, - ): void; - (e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void; + ( + e: "pickGenericDoc", + payload: { genericDoc: GenericDocForAccompanyingPeriod }, + ): void; + (e: "removeAttachment", payload: { attachment: WorkflowAttachment }): void; }>(); type PickGenericModalType = InstanceType; @@ -28,66 +28,66 @@ const pickDocModal = useTemplateRef("pickDocModal"); const props = defineProps(); const attachedGenericDoc = computed( - () => - props.attachments - .map((a: WorkflowAttachment) => a.genericDoc) - .filter( - (g: GenericDoc | null) => g !== null, - ) as GenericDocForAccompanyingPeriod[], + () => + props.attachments + .map((a: WorkflowAttachment) => a.genericDoc) + .filter( + (g: GenericDoc | null) => g !== null, + ) as GenericDocForAccompanyingPeriod[], ); const workflow = ref(null); onMounted(async () => { - workflow.value = await fetchWorkflow(Number(props.workflowId)); - console.log("workflow", workflow.value); + workflow.value = await fetchWorkflow(Number(props.workflowId)); + console.log("workflow", workflow.value); }); const openModal = function () { - pickDocModal.value?.openModal(); + pickDocModal.value?.openModal(); }; const onPickGenericDoc = ({ - genericDoc, + genericDoc, }: { - genericDoc: GenericDocForAccompanyingPeriod; + genericDoc: GenericDocForAccompanyingPeriod; }) => { - emit("pickGenericDoc", { genericDoc }); + emit("pickGenericDoc", { genericDoc }); }; const onRemoveAttachment = (payload: { attachment: WorkflowAttachment }) => { - emit("removeAttachment", payload); + emit("removeAttachment", payload); }; const canEditAttachement = computed(() => { - if (null === workflow.value) { - return false; - } + if (null === workflow.value) { + return false; + } - return workflow.value._permissions.CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT; + return workflow.value._permissions.CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT; }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/AttachmentList.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/AttachmentList.vue index d12496266..f9d29d1b6 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/AttachmentList.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/AttachmentList.vue @@ -1,9 +1,9 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue index d57a99475..2d769121f 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/GenericDocItemBox.vue @@ -2,17 +2,17 @@ import { GenericDoc } from "ChillDocStoreAssets/types/generic_doc"; interface GenericDocItemBoxProps { - genericDoc: GenericDoc; + genericDoc: GenericDoc; } const props = defineProps(); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue index 717b8a62b..ec0296da4 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDoc.vue @@ -1,7 +1,7 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocItem.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocItem.vue index 2e1cf6d04..51c7dbc4b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocItem.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocItem.vue @@ -4,93 +4,85 @@ import GenericDocItemBox from "ChillMainAssets/vuejs/WorkflowAttachment/Componen import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue"; interface PickGenericDocItemProps { - genericDoc: GenericDocForAccompanyingPeriod; - accompanyingPeriodId: number; - isPicked: boolean; + genericDoc: GenericDocForAccompanyingPeriod; + accompanyingPeriodId: number; + isPicked: boolean; } const props = defineProps(); const emit = defineEmits<{ - ( - e: "pickGenericDoc", - payload: { genericDoc: GenericDocForAccompanyingPeriod }, - ): void; + ( + e: "pickGenericDoc", + payload: { genericDoc: GenericDocForAccompanyingPeriod }, + ): void; - ( - e: "removeGenericDoc", - payload: { genericDoc: GenericDocForAccompanyingPeriod }, - ): void; + ( + e: "removeGenericDoc", + payload: { genericDoc: GenericDocForAccompanyingPeriod }, + ): void; }>(); const clickOnAddButton = () => { - emit("pickGenericDoc", { genericDoc: props.genericDoc }); + emit("pickGenericDoc", { genericDoc: props.genericDoc }); }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue index adaa4c825..c30afa5dc 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/Component/PickGenericDocModal.vue @@ -6,20 +6,20 @@ import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/gener import { EntityWorkflow } from "ChillMainAssets/types"; interface PickGenericDocModalProps { - workflow: EntityWorkflow | null; - accompanyingPeriodId: number; - toRemove: GenericDocForAccompanyingPeriod[]; + workflow: EntityWorkflow | null; + accompanyingPeriodId: number; + toRemove: GenericDocForAccompanyingPeriod[]; } type PickGenericDocType = InstanceType; const props = defineProps(); const emit = defineEmits<{ - // eslint-disable-next-line @typescript-eslint/prefer-function-type - ( - e: "pickGenericDoc", - payload: { genericDoc: GenericDocForAccompanyingPeriod }, - ): void; + // eslint-disable-next-line @typescript-eslint/prefer-function-type + ( + e: "pickGenericDoc", + payload: { genericDoc: GenericDocForAccompanyingPeriod }, + ): void; }>(); const picker = useTemplateRef("picker"); const modalOpened = ref(false); @@ -29,88 +29,88 @@ const modalClasses = { "modal-xl": true, "modal-dialog-scrollable": true }; const numberOfPicked = computed(() => pickeds.value.length); const onPicked = ({ - genericDoc, + genericDoc, }: { - genericDoc: GenericDocForAccompanyingPeriod; + genericDoc: GenericDocForAccompanyingPeriod; }) => { - pickeds.value.push(genericDoc); + pickeds.value.push(genericDoc); }; const onRemove = ({ - genericDoc, + genericDoc, }: { - genericDoc: GenericDocForAccompanyingPeriod; + genericDoc: GenericDocForAccompanyingPeriod; }) => { - const index = pickeds.value.findIndex( - (item) => item.uniqueKey === genericDoc.uniqueKey, - ); + const index = pickeds.value.findIndex( + (item) => item.uniqueKey === genericDoc.uniqueKey, + ); - if (index === -1) { - throw new Error("Remove generic doc that doesn't exist"); - } + if (index === -1) { + throw new Error("Remove generic doc that doesn't exist"); + } - pickeds.value.splice(index, 1); + pickeds.value.splice(index, 1); }; const onConfirm = () => { - for (let genericDoc of pickeds.value) { - emit("pickGenericDoc", { genericDoc }); - } - pickeds.value = []; - closeModal(); + for (let genericDoc of pickeds.value) { + emit("pickGenericDoc", { genericDoc }); + } + pickeds.value = []; + closeModal(); }; const closeModal = function () { - modalOpened.value = false; + modalOpened.value = false; }; const openModal = function () { - modalOpened.value = true; + modalOpened.value = true; }; defineExpose({ openModal, closeModal }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/index.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/index.ts index 872e6c251..de3d2e3ef 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/WorkflowAttachment/index.ts @@ -3,107 +3,102 @@ import App from "./App.vue"; import { _createI18n } from "../_js/i18n"; import { WorkflowAttachment } from "ChillMainAssets/types"; import { - create_attachment, - delete_attachment, - find_attachments_by_workflow, + create_attachment, + delete_attachment, + find_attachments_by_workflow, } from "ChillMainAssets/lib/workflow/attachments"; import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc"; import ToastPlugin from "vue-toast-notification"; import "vue-toast-notification/dist/theme-bootstrap.css"; window.addEventListener("DOMContentLoaded", () => { - const attachments = document.querySelectorAll( - 'div[data-app="workflow_attachments"]', + const attachments = document.querySelectorAll( + 'div[data-app="workflow_attachments"]', + ); + + attachments.forEach(async (el) => { + const workflowId = parseInt(el.dataset.entityWorkflowId || ""); + const accompanyingPeriodId = parseInt( + el.dataset.relatedAccompanyingPeriodId || "", ); + const attachments = await find_attachments_by_workflow(workflowId); - attachments.forEach(async (el) => { - const workflowId = parseInt(el.dataset.entityWorkflowId || ""); - const accompanyingPeriodId = parseInt( - el.dataset.relatedAccompanyingPeriodId || "", - ); - const attachments = await find_attachments_by_workflow(workflowId); + const app = createApp({ + template: + '', + components: { App }, + data: function () { + return { workflowId, accompanyingPeriodId, attachments }; + }, + methods: { + onRemoveAttachment: async function ({ + attachment, + }: { + attachment: WorkflowAttachment; + }): Promise { + const index = this.$data.attachments.findIndex( + (el: WorkflowAttachment) => el.id === attachment.id, + ); + if (-1 === index) { + console.warn( + "this attachment is not associated with the workflow", + attachment, + ); + this.$toast.error( + "This attachment is not associated with the workflow", + ); + return; + } - const app = createApp({ - template: - '', - components: { App }, - data: function () { - return { workflowId, accompanyingPeriodId, attachments }; - }, - methods: { - onRemoveAttachment: async function ({ - attachment, - }: { - attachment: WorkflowAttachment; - }): Promise { - const index = this.$data.attachments.findIndex( - (el: WorkflowAttachment) => el.id === attachment.id, - ); - if (-1 === index) { - console.warn( - "this attachment is not associated with the workflow", - attachment, - ); - this.$toast.error( - "This attachment is not associated with the workflow", - ); - return; - } + try { + await delete_attachment(attachment); + } catch (error) { + console.error(error); + this.$toast.error("Error while removing element"); + throw error; + } + this.$data.attachments.splice(index, 1); + this.$toast.success("Pièce jointe supprimée"); + }, + onPickGenericDoc: async function ({ + genericDoc, + }: { + genericDoc: GenericDocForAccompanyingPeriod; + }): Promise { + console.log("picked generic doc", genericDoc); - try { - await delete_attachment(attachment); - } catch (error) { - console.error(error); - this.$toast.error("Error while removing element"); - throw error; - } - this.$data.attachments.splice(index, 1); - this.$toast.success("Pièce jointe supprimée"); - }, - onPickGenericDoc: async function ({ - genericDoc, - }: { - genericDoc: GenericDocForAccompanyingPeriod; - }): Promise { - console.log("picked generic doc", genericDoc); + // prevent to create double attachment: + if ( + -1 !== + this.$data.attachments.findIndex( + (el: WorkflowAttachment) => + el.genericDoc?.key === genericDoc.key && + JSON.stringify(el.genericDoc?.identifiers) == + JSON.stringify(genericDoc.identifiers), + ) + ) { + console.warn( + "this document is already attached to the workflow", + genericDoc, + ); + this.$toast.error("Ce document est déjà attaché au workflow"); + return; + } - // prevent to create double attachment: - if ( - -1 !== - this.$data.attachments.findIndex( - (el: WorkflowAttachment) => - el.genericDoc?.key === genericDoc.key && - JSON.stringify(el.genericDoc?.identifiers) == - JSON.stringify(genericDoc.identifiers), - ) - ) { - console.warn( - "this document is already attached to the workflow", - genericDoc, - ); - this.$toast.error( - "Ce document est déjà attaché au workflow", - ); - return; - } - - try { - const attachment = await create_attachment( - workflowId, - genericDoc, - ); - this.$data.attachments.push(attachment); - } catch (error) { - console.error(error); - throw error; - } - }, - }, - }); - const i18n = _createI18n({}); - app.use(i18n); - app.use(ToastPlugin); - - app.mount(el); + try { + const attachment = await create_attachment(workflowId, genericDoc); + this.$data.attachments.push(attachment); + } catch (error) { + console.error(error); + throw error; + } + }, + }, }); + const i18n = _createI18n({}); + app.use(i18n); + app.use(ToastPlugin); + + app.mount(el); + }); }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue index e5838089e..026e55c1f 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsButton.vue @@ -1,23 +1,24 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue index 41bb00025..46ec986aa 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressDetailsContent.vue @@ -1,16 +1,16 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressModal.vue index 7c00066be..96e32ba04 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressModal.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/AddressModal.vue @@ -1,17 +1,17 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsGeographicalLayers.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsGeographicalLayers.vue index c104df1dc..e740cce17 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsGeographicalLayers.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsGeographicalLayers.vue @@ -1,59 +1,59 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue index 3fe075fcc..e3503c9e4 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsMap.vue @@ -1,20 +1,17 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsRefMatching.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsRefMatching.vue index c3d421411..483f17941 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsRefMatching.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddressDetails/Parts/AddressDetailsRefMatching.vue @@ -1,140 +1,124 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/BadgeEntity.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/BadgeEntity.vue index f8cd21c55..69df000dd 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/BadgeEntity.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/BadgeEntity.vue @@ -1,67 +1,64 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/CommentEditor/CommentEditor.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/CommentEditor/CommentEditor.vue index d6019fabe..f9b75510a 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/CommentEditor/CommentEditor.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/CommentEditor/CommentEditor.vue @@ -1,122 +1,125 @@ + + - - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue index 13459b390..9c010e8d1 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Confidential.vue @@ -1,40 +1,40 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue index 86f4155b6..7085b3b18 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/AddressRenderBox.vue @@ -1,83 +1,77 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue index d47b9b777..318d4faaa 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/GenderIconRenderBox.vue @@ -1,9 +1,28 @@ - diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue index 7c37e98a9..81f836c62 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserGroupRenderBox.vue @@ -4,23 +4,23 @@ import { computed } from "vue"; import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper"; interface UserGroupRenderBoxProps { - userGroup: UserGroup; + userGroup: UserGroup; } const props = defineProps(); const styles = computed<{ color: string; "background-color": string }>(() => { - return { - color: props.userGroup.foregroundColor, - "background-color": props.userGroup.backgroundColor, - }; + return { + color: props.userGroup.foregroundColor, + "background-color": props.userGroup.backgroundColor, + }; }); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue index 63c43c37f..4bae3b541 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Entity/UserRenderBoxBadge.vue @@ -1,31 +1,31 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue index a9e5348ea..63f762302 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/EntityWorkflowVueSubscriber.vue @@ -1,79 +1,71 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue index 59dcd80f4..658c0f9e9 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue @@ -1,49 +1,45 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue index 7ec7f47e2..ee09270ec 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue @@ -1,42 +1,39 @@ @@ -73,19 +79,19 @@ const emits = defineEmits<{ * This is a mask behind the modal. */ .modal-mask { - position: fixed; - z-index: 9998; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.75); - transition: opacity 0.3s ease; + position: fixed; + z-index: 9998; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.75); + transition: opacity 0.3s ease; } .modal-header .close { - border-top-right-radius: 0.3rem; - margin-right: 0; - margin-left: auto; + border-top-right-radius: 0.3rem; + margin-right: 0; + margin-left: auto; } /* * The following styles are auto-applied to elements with @@ -96,23 +102,23 @@ const emits = defineEmits<{ * these styles. */ .modal-enter { - opacity: 0; + opacity: 0; } .modal-leave-active { - opacity: 0; + opacity: 0; } .modal-enter .modal-container, .modal-leave-active .modal-container { - -webkit-transform: scale(1.1); - transform: scale(1.1); + -webkit-transform: scale(1.1); + transform: scale(1.1); } h3.modal-title { - font-size: 1.5rem; - font-weight: bold; + font-size: 1.5rem; + font-weight: bold; } div.modal-footer { - button:first-child { - margin-right: auto; - } + button:first-child { + margin-right: auto; + } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadAllToggle.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadAllToggle.vue index 57f7fd7f8..bb7a43c92 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadAllToggle.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadAllToggle.vue @@ -1,17 +1,17 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadToggle.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadToggle.vue index cc64d4835..fb94af80f 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadToggle.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Notification/NotificationReadToggle.vue @@ -1,96 +1,96 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/WaitingScreen.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/WaitingScreen.vue index 963db5e81..ea12b1577 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/WaitingScreen.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/WaitingScreen.vue @@ -2,61 +2,61 @@ import { WaitingScreenState } from "ChillMainAssets/types"; interface Props { - state: WaitingScreenState; + state: WaitingScreenState; } const props = defineProps(); diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_composables/violationList.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_composables/violationList.ts new file mode 100644 index 000000000..b8af0b519 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_composables/violationList.ts @@ -0,0 +1,78 @@ +import { ref } from "vue"; +import { ValidationExceptionInterface } from "ChillMainAssets/types"; + +export function useViolationList< + T extends Record>, +>() { + type ViolationKey = Extract; + const violationsList = ref | null>(null); + + function violationTitles

    (property: P): string[] { + if (null === violationsList.value) { + return []; + } + const r = violationsList.value + .violationsByNormalizedProperty(property) + .map((v) => v.title); + + return r; + } + function violationTitlesWithParameter< + P extends ViolationKey, + Param extends Extract, + >( + property: P, + with_parameter: Param, + with_parameter_value: T[P][Param], + ): string[] { + if (violationsList.value === null) { + return []; + } + return violationsList.value + .violationsByNormalizedPropertyAndParams( + property, + with_parameter, + with_parameter_value, + ) + .map((v) => v.title); + } + + function hasViolation

    (property: P): boolean { + return violationTitles(property).length > 0; + } + function hasViolationWithParameter< + P extends ViolationKey, + Param extends Extract, + >( + property: P, + with_parameter: Param, + with_parameter_value: T[P][Param], + ): boolean { + return ( + violationTitlesWithParameter( + property, + with_parameter, + with_parameter_value, + ).length > 0 + ); + } + + function setValidationException>( + validationException: V, + ): void { + violationsList.value = validationException; + } + + function cleanException(): void { + violationsList.value = null; + } + + return { + violationTitles, + violationTitlesWithParameter, + setValidationException, + cleanException, + hasViolationWithParameter, + hasViolation, + }; +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts index db9a37104..cbee6bf25 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.ts @@ -2,87 +2,86 @@ import { createI18n } from "vue-i18n"; import datetimeFormats from "../i18n/datetimeFormats"; const messages = { - fr: { - action: { - actions: "Actions", - show: "Voir", - edit: "Modifier", - create: "Créer", - remove: "Enlever", - delete: "Supprimer", - save: "Enregistrer", - valid: "Valider", - valid_and_see: "Valider et voir", - add: "Ajouter", - show_modal: "Ouvrir une modale", - ok: "OK", - cancel: "Annuler", - close: "Fermer", - back: "Retour", - check_all: "cocher tout", - reset: "réinitialiser", - redirect: { - person: "Quitter la page et ouvrir la fiche de l'usager", - thirdparty: "Quitter la page et voir le tiers", - }, - refresh: "Rafraîchir", - addContact: "Ajouter un contact", - }, - nav: { - next: "Suivant", - previous: "Précédent", - top: "Haut", - bottom: "Bas", - }, - renderbox: { - person: "Usager", - birthday: { - man: "Né le", - woman: "Née le", - neutral: "Né·e le", - unknown: "Né·e le", - }, - deathdate: "Date de décès", - household_without_address: "Le ménage de l'usager est sans adresse", - no_data: "Aucune information renseignée", - type: { - thirdparty: "Tiers", - person: "Usager", - }, - holder: "Titulaire", - years_old: "1 an | {n} an | {n} ans", - residential_address: "Adresse de résidence", - located_at: "réside chez", - }, + fr: { + action: { + actions: "Actions", + show: "Voir", + edit: "Modifier", + create: "Créer", + remove: "Enlever", + delete: "Supprimer", + save: "Enregistrer", + valid: "Valider", + valid_and_see: "Valider et voir", + add: "Ajouter", + show_modal: "Ouvrir une modale", + ok: "OK", + cancel: "Annuler", + close: "Fermer", + back: "Retour", + check_all: "cocher tout", + reset: "réinitialiser", + redirect: { + person: "Quitter la page et ouvrir la fiche de l'usager", + thirdparty: "Quitter la page et voir le tiers", + }, + refresh: "Rafraîchir", + addContact: "Ajouter un contact", }, + nav: { + next: "Suivant", + previous: "Précédent", + top: "Haut", + bottom: "Bas", + }, + renderbox: { + person: "Usager", + birthday: { + man: "Né le", + woman: "Née le", + neutral: "Né·e le", + unknown: "Né·e le", + }, + deathdate: "Date de décès", + household_without_address: "Le ménage de l'usager est sans adresse", + no_data: "Aucune information renseignée", + type: { + thirdparty: "Tiers", + person: "Usager", + }, + holder: "Titulaire", + years_old: "1 an | {n} an | {n} ans", + residential_address: "Adresse de résidence", + located_at: "réside chez", + }, + }, }; const _createI18n = (appMessages: any, legacy?: boolean) => { - Object.assign(messages.fr, appMessages.fr); - return createI18n({ - legacy: typeof legacy === undefined ? true : legacy, - locale: "fr", - fallbackLocale: "fr", - // @ts-ignore - datetimeFormats, - messages, - }); + Object.assign(messages.fr, appMessages.fr); + return createI18n({ + legacy: typeof legacy === undefined ? true : legacy, + locale: "fr", + fallbackLocale: "fr", + // @ts-ignore + datetimeFormats, + messages, + }); }; export { _createI18n }; export const multiSelectMessages = { - fr: { - multiselect: { - placeholder: "Choisir", - tag_placeholder: "Créer un nouvel élément", - select_label: '"Entrée" ou cliquez pour sélectionner', - deselect_label: '"Entrée" ou cliquez pour désélectionner', - select_group_label: - 'Appuyer sur "Entrée" pour sélectionner ce groupe', - deselect_group_label: - 'Appuyer sur "Entrée" pour désélectionner ce groupe', - selected_label: "Sélectionné", - }, + fr: { + multiselect: { + placeholder: "Choisir", + tag_placeholder: "Créer un nouvel élément", + select_label: '"Entrée" ou cliquez pour sélectionner', + deselect_label: '"Entrée" ou cliquez pour désélectionner', + select_group_label: 'Appuyer sur "Entrée" pour sélectionner ce groupe', + deselect_group_label: + 'Appuyer sur "Entrée" pour désélectionner ce groupe', + selected_label: "Sélectionné", }, + }, }; diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/i18n/datetimeFormats.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/i18n/datetimeFormats.ts index fe20cb217..aca1328ac 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/i18n/datetimeFormats.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/i18n/datetimeFormats.ts @@ -1,27 +1,27 @@ export default { - fr: { - short: { - year: "numeric", - month: "numeric", - day: "numeric", - }, - text: { - year: "numeric", - month: "long", - day: "numeric", - }, - long: { - year: "numeric", - month: "numeric", - day: "numeric", - hour: "numeric", - minute: "numeric", - hour12: false, - }, - hoursOnly: { - hour: "numeric", - minute: "numeric", - hour12: false, - }, + fr: { + short: { + year: "numeric", + month: "numeric", + day: "numeric", }, + text: { + year: "numeric", + month: "long", + day: "numeric", + }, + long: { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "numeric", + minute: "numeric", + hour12: false, + }, + hoursOnly: { + hour: "numeric", + minute: "numeric", + hour12: false, + }, + }, }; diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig index 98af21171..27214da68 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -11,5 +11,10 @@ {% endblock %} {% block js %} + + {{ encore_entry_script_tags('page_homepage_widget') }} + {% endblock %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Menu/section.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Menu/section.html.twig index 67683bf50..56bb9a2a9 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Menu/section.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Menu/section.html.twig @@ -1,34 +1,16 @@ -{# - * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, - / - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . -#} -