Compare commits

...

255 Commits

Author SHA1 Message Date
Pol Dellaiera
bddb6e615e CS: Autofix. 2022-02-01 09:35:04 +01:00
Pol Dellaiera
a18ea30c8f chill-main work in progress 2022-02-01 09:35:04 +01:00
Pol Dellaiera
2ba240525c Add missing form type wirings. 2022-02-01 09:35:04 +01:00
Pol Dellaiera
5b76338fb0 Add new PhoneNumberType. 2022-02-01 09:35:04 +01:00
Pol Dellaiera
ce8399945a Add new configuration property. 2022-02-01 09:35:04 +01:00
Pol Dellaiera
1605bc5d08 New migration file. 2022-02-01 09:35:03 +01:00
Pol Dellaiera
ebdcc5a07d fix: Update namespace to prevent having issues with PSR4 autoloading. 2022-02-01 09:11:14 +01:00
Pol Dellaiera
0f31a49890 fix: Update namespace to prevent having issues with PSR4 autoloading. 2022-02-01 09:10:32 +01:00
170ce2d51c minor fix and renaming 2022-01-31 16:55:19 +01:00
971789d5cf Merge remote-tracking branch 'origin/master' into foldlist_in_entity 2022-01-31 15:51:21 +01:00
72815e4b70 Merge branch 'homepage/rewrite' 2022-01-31 15:40:40 +01:00
44183957b8 Merge remote-tracking branch 'origin/master' into homepage/rewrite 2022-01-31 15:36:53 +01:00
5f6cffa08a fix regression in serach results appareance in addpersons 2022-01-31 14:49:23 +01:00
222dae3c32 Merge remote-tracking branch 'origin/master' into issue420_closeModalAddPerson 2022-01-31 14:24:45 +01:00
08c13b8c98 Merge remote-tracking branch 'origin/master' into issue412_413_activity 2022-01-31 14:22:34 +01:00
1bc2500b28 update changelog [ci-skip] 2022-01-31 14:18:10 +01:00
f648a9351b workflow: fix some stuffs 2022-01-31 14:17:10 +01:00
ffe466a334 fix icon for searching 2022-01-31 14:16:33 +01:00
nobohan
3978e7c959 upd CHANGELOG 2022-01-31 13:48:21 +01:00
nobohan
2eb5c45a4d accompanying course: fetch scopes only for app (not for banner) 2022-01-31 13:46:43 +01:00
nobohan
fefe208260 accompanying course: fetch users and suggested referrers only for app, not for banner 2022-01-31 13:32:34 +01:00
c14101714c update changelog 2022-01-31 13:09:13 +01:00
8ec5636c57 Merge remote-tracking branch 'origin/master' into household-editor/push-to-household 2022-01-31 13:07:26 +01:00
fba7060a91 update changelog 2022-01-31 13:05:41 +01:00
6894fa7101 Merge branch 'issue389_add_age' into 'master'
add age/ person text component

See merge request Chill-Projet/chill-bundles!308
2022-01-31 12:04:48 +00:00
41fa1d9ca6 fix cs 2022-01-31 13:02:43 +01:00
f4bca2f410 add person text in accompanying period work 2022-01-31 13:00:48 +01:00
6198891202 add anchor to retrieve evaluations section in accompanyingCourseWorkEdit page 2022-01-31 12:47:31 +01:00
c806c06279 homepage_widget: basically build tables for evaluations, tasks and courses 2022-01-31 12:46:44 +01:00
e04c02055c add age in some place and re-organize some usage of the component 2022-01-31 12:45:50 +01:00
ac5675933d Merge branch 'issue411_openingDate_parcours' into 'master'
Opening date of parcour

See merge request Chill-Projet/chill-bundles!313
2022-01-31 11:07:07 +00:00
e7db71b0f3 fast actions are custom overrided buttons 2022-01-31 12:03:42 +01:00
nobohan
1967fc4bed upd CHANGELOG 2022-01-31 12:01:52 +01:00
nobohan
befd5dac42 person: accompanying course: treat validation error when editing on-the-fly entities 2022-01-31 12:00:40 +01:00
186b8847d9 Merge remote-tracking branch 'origin/issue389_add_age' into issue389_add_age 2022-01-31 11:46:07 +01:00
90d0cbc3b1 Merge remote-tracking branch 'origin/master' into issue389_add_age 2022-01-31 11:44:55 +01:00
ac3d39b151 Merge branch 'master' into 'issue389_add_age'
# Conflicts:
#   CHANGELOG.md
2022-01-31 10:25:32 +00:00
efb5bd64b4 notification-list: todo add a comment counter 2022-01-31 11:25:23 +01:00
76a7b019eb Merge branch 'master' into 'issue411_openingDate_parcours'
# Conflicts:
#   CHANGELOG.md
2022-01-31 10:19:54 +00:00
230d73498e workflow list: mask title 2022-01-31 11:02:01 +01:00
nobohan
a68a43adc0 upd CHANGELOG 2022-01-31 10:55:11 +01:00
nobohan
5a75e38f33 accompanying course: close modal when edit participation 2022-01-31 10:48:04 +01:00
1c02eb23fd fix user search 2022-01-31 10:41:08 +01:00
1ba5df1280 hop 2022-01-31 09:45:37 +01:00
73c4a5d39d better color coherency with buttons 2022-01-30 19:45:09 +01:00
0a58c43952 fix date format on popover 2022-01-30 19:43:17 +01:00
a3b823d33f improve workflow button 2022-01-30 16:43:09 +01:00
de45555c5a add popover html content 2022-01-30 16:35:47 +01:00
0add020e57 workflows in entity: other vue component call workflow modal 2022-01-30 13:53:51 +01:00
84a76d2c76 ListWorkflow: styles for list (like in twig) 2022-01-30 13:36:37 +01:00
d88207132b workflows in entity: twig call once vue component that list in modal with add button, or just display add button if none 2022-01-30 11:26:04 +01:00
932d0e86d9 Merge branch 'foldlist_in_entity' of gitlab.com:Chill-Projet/chill-bundles into foldlist_in_entity 2022-01-29 12:37:48 +01:00
671f1223b5 Merge branch 'issue_mes_parcours' into 'master'
Mes parcours

See merge request Chill-Projet/chill-bundles!307
2022-01-29 02:09:04 +00:00
1a04d903fc fixes for page 'mes parcours' 2022-01-29 03:08:34 +01:00
125dd4d980 Merge remote-tracking branch 'origin/master' into issue_mes_parcours 2022-01-29 02:58:32 +01:00
2ddab027ed Merge remote-tracking branch 'origin/master' into issue410_restyle_parcourslist_personSearch 2022-01-29 02:57:05 +01:00
1724400d7c button requestor on the right 2022-01-29 02:56:06 +01:00
d1a0934bb1 search household in household mmebers editor 2022-01-29 02:38:47 +01:00
bc90664480 api endpoint for search household 2022-01-29 01:14:30 +01:00
99dc7490cb missing translation 2022-01-29 00:33:09 +01:00
5ac485e06e example of workflow + finalize normalization 2022-01-29 00:20:35 +01:00
dc184762d6 workflow history 2022-01-29 00:20:35 +01:00
86e7b0f007 rewrite workflow and handle finalize differently 2022-01-29 00:20:35 +01:00
fdafe7c82b normalizer for entityworkflow 2022-01-29 00:20:35 +01:00
d285d84068 wip 2022-01-28 22:30:18 +01:00
da22532587 cancel comments in entity notifications 2022-01-28 22:27:30 +01:00
b83ad77fd8 homepage_widget: details + courses table 2022-01-28 18:22:33 +01:00
00ac6aa1b9 homepage_widget: init counters on load, arrange MyCustoms tab 2022-01-28 18:10:48 +01:00
318c001904 changelog updated 2022-01-28 18:07:20 +01:00
e2d406b97b php validation added 2022-01-28 18:06:13 +01:00
nobohan
efff496f7a hot fix: pick-entity: set input.value to null if multiple=false 2022-01-28 18:01:29 +01:00
fe4eaa92be fix bug display current openingDate in datepicker 2022-01-28 17:36:59 +01:00
cb17a7e8a5 Visual improvements of date input fields in social action create form 2022-01-28 17:06:09 +01:00
ad4153a07e New component for parcours startDate 2022-01-28 17:05:41 +01:00
e95093f144 Merge branch 'homepage/rewrite' of gitlab.com:Chill-Projet/chill-bundles into homepage/rewrite 2022-01-28 16:36:01 +01:00
59cdf07c7e add form in notification thread inside entities show 2022-01-28 16:23:41 +01:00
292c9380ad endpoint for recent accompanying period attributions 2022-01-28 15:39:37 +01:00
701ad17299 Merge branch 'homepage/rewrite' of gitlab.com:Chill-Projet/chill-bundles into homepage/rewrite 2022-01-28 15:24:05 +01:00
7f41f14959 resume page: move notify button in masonry bloc and notification list below in content 2022-01-28 15:21:44 +01:00
fcd5fba13e add history for user in accompanying period (+ counter) 2022-01-28 14:42:18 +01:00
2c57eab4d2 workflows list: rename tabs translations 2022-01-28 14:18:11 +01:00
2cd51eed2e Merge remote-tracking branch 'origin/master' into homepage/rewrite 2022-01-28 14:13:28 +01:00
72e306b583 change add notification button color 2022-01-28 12:46:58 +01:00
917dd49d07 workflows: add freeze mechanism on breadcrumbs, edit document button, and history 2022-01-28 12:41:44 +01:00
71ca912749 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2022-01-28 12:25:06 +01:00
4620e32b82 changelog updated 2022-01-28 12:20:29 +01:00
0cda5d637d styling of parcours list in person search changed 2022-01-28 12:16:32 +01:00
646f39b9ed Merge conflicts fixed 2022-01-28 12:15:51 +01:00
nobohan
105fe16122 upd CHANGELOG 2022-01-28 11:40:30 +01:00
nobohan
4d1a553474 show activity attendee (présence) in the activity list 2022-01-28 11:40:08 +01:00
5bfdee0c28 corrections review 2022-01-28 11:09:14 +01:00
ae33392ad0 update changelog 2022-01-28 10:58:24 +01:00
0e0fe90277 Merge branch 'issue399_dynamicPicker_asideActivity' into 'master'
dynamic picker aside activity

See merge request Chill-Projet/chill-bundles!305
2022-01-28 09:55:27 +00:00
792eafce72 dynamic picker aside activity 2022-01-28 09:55:26 +00:00
616d01a6c9 Merge branch 'issue403_residential_address' into 'master'
residentialAddress

See merge request Chill-Projet/chill-bundles!302
2022-01-28 09:48:00 +00:00
juminet
21edc74ada residentialAddress 2022-01-28 09:48:00 +00:00
ab23290599 refund extension_list_notification template: change place and content 2022-01-28 10:22:48 +01:00
1a2e1eaf2a put notification comments in a separate include 2022-01-28 10:22:17 +01:00
feaee8a0b1 a few more implementations in twig templates 2022-01-28 10:07:34 +01:00
0a26e7f326 using serialized age instead of function + translation fix 2022-01-28 09:57:14 +01:00
ebf85d4e4d Merge branch 'homepage/rewrite' of gitlab.com:Chill-Projet/chill-bundles into homepage/rewrite 2022-01-28 09:56:52 +01:00
72cffd7a3d minor changes 2022-01-28 09:56:20 +01:00
65418b17ce Merge branch 'issues_362_filiations' into 'master'
filiations: small adjustments (colors, labels, ...)

See merge request Chill-Projet/chill-bundles!309
2022-01-28 08:49:59 +00:00
juminet
8432c215a3 filiations: small adjustments (colors, labels, ...) 2022-01-28 08:49:59 +00:00
nobohan
87e1f8f077 hot fix: initialize typed comment in CommentEmbeddable 2022-01-28 09:47:36 +01:00
nobohan
33f393203e activity admin: change validation rule for social action visible field 2022-01-27 19:43:08 +01:00
6aa1e136b4 fix bug when suggested person is thirdparty 2022-01-27 18:14:32 +01:00
10a9b6c909 using normalizer for age and added obele 2022-01-27 18:13:46 +01:00
ce912e4405 ajout compteurs 2022-01-27 17:28:59 +01:00
ab11d3e8b3 Merge branch 'issue400_sort_activity' into 'master'
activity: order activities by date and by id

See merge request Chill-Projet/chill-bundles!301
2022-01-27 16:12:08 +00:00
juminet
445fe0cee4 activity: order activities by date and by id 2022-01-27 16:12:07 +00:00
7c6c8de456 Merge branch 'issue394_address_confidential' into 'master'
confidential address

See merge request Chill-Projet/chill-bundles!299
2022-01-27 16:08:58 +00:00
0378df42b6 fix cs 2022-01-27 17:08:00 +01:00
ad8d40cb1c work on confidential / blur module 2022-01-27 16:52:46 +01:00
f4516f8369 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2022-01-27 16:31:41 +01:00
5b25f82963 changelog updated 2022-01-27 16:24:35 +01:00
8720cc730e implementation of person-text 2022-01-27 16:19:19 +01:00
44d1535bfb style fixes 2022-01-27 16:18:54 +01:00
8d663cdee6 creation of vue component person-text 2022-01-27 16:18:35 +01:00
7c8b08c3a7 Merge branch 'master' into issue394_address_confidential 2022-01-27 15:39:10 +01:00
6df570d96c set referrer app 2022-01-27 15:22:15 +01:00
3ce650eea6 Merge branch 'issue389_add_age' of gitlab.com:Chill-Projet/chill-bundles into issue389_add_age 2022-01-27 13:03:02 +01:00
6ef7d9b47b adding age in renderbox and renderstring, implementation of renderbox option in household summary 2022-01-27 12:59:14 +01:00
af3847366b adding age in renderbox and renderstring, implementation of renderbox option in household summary 2022-01-27 12:33:50 +01:00
d70f8aa712 phpstan fixes 2022-01-27 11:50:26 +01:00
14463dcd38 changelog updated 2022-01-27 11:45:03 +01:00
b9c2d63a53 mes parcours page created 2022-01-27 11:42:14 +01:00
3fd4c6339a Merge conflicts resolved 2022-01-27 11:34:57 +01:00
4d76de5f9f fix normalization for person in docgen 2022-01-26 23:52:27 +01:00
0a92ad905b variables for docgen 2022-01-26 23:49:24 +01:00
7af7135971 use trait to implement TrackCreationInterface and TrackUpdateInterface 2022-01-26 21:51:13 +01:00
c70a4dc664 fix loading of evaluations 2022-01-26 21:46:07 +01:00
dc0fae7549 fixes person resources 2022-01-26 17:53:09 +01:00
b38924cc3d Revert "Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles"
This reverts commit 58c4e37116.
2022-01-26 16:38:58 +01:00
58c4e37116 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2022-01-26 16:04:09 +01:00
3656825d20 bug fix in form and controller 2022-01-26 15:56:10 +01:00
39a7cecd24 homepage_widget: improve notifications rows 2022-01-26 15:44:17 +01:00
6d382f93e8 use pickDynamicUserType 2022-01-26 15:08:47 +01:00
6e554e74ab Merge branch 'issue382_person_resource' 2022-01-26 14:43:50 +01:00
4c125865cf fix of twig if condition for person resources 2022-01-26 14:21:39 +01:00
12c68a4c9f Merge branch 'issue406_truncate_comment' into 'master'
Truncate comment

See merge request Chill-Projet/chill-bundles!304
2022-01-26 13:06:04 +00:00
66ac5c7435 Merge remote-tracking branch 'origin/master' into issue406_truncate_comment 2022-01-26 14:03:49 +01:00
e4b46a188c Merge branch 'master' into issue406_truncate_comment 2022-01-26 14:01:42 +01:00
7a78e8a6a7 fixes for pesonnes ressources 2022-01-26 14:00:45 +01:00
4496836cb2 Merge branch 'issue382_person_resource' into 'master'
Creation of PersonResource

See merge request Chill-Projet/chill-bundles!291
2022-01-26 12:52:15 +00:00
88d1fe24b4 Creation of PersonResource 2022-01-26 12:52:15 +00:00
0ea1708fe9 Merge branch 'master' into issue382_person_resource 2022-01-26 13:50:58 +01:00
a9b7cf93e9 adaptation for list for regulation 2022-01-26 13:50:19 +01:00
5d36e56641 changelog updated 2022-01-26 12:57:14 +01:00
f00dfad98e csfixes 2022-01-26 12:56:02 +01:00
7943c22ae7 comments truncated and link added to comment page 2022-01-26 12:55:48 +01:00
d365fcea39 merge conflict in changelog resolved 2022-01-26 12:13:37 +01:00
9f9f9d7881 csfixes 2022-01-26 12:10:51 +01:00
20f814766c merge fixes 2022-01-26 12:10:18 +01:00
ed00a882af cs-fixes 2022-01-26 12:08:18 +01:00
5d7501d36e violation added if all three association entities are null 2022-01-26 12:08:18 +01:00
ab5f9923a6 CSfixer changes 2022-01-26 12:08:18 +01:00
1a5e8b9d7b Fix user/thirdparty/freetext field in edit form 2022-01-26 12:08:18 +01:00
f7f2bb0caa reorganize templates to fix edit form 2022-01-26 12:08:18 +01:00
bf7fcfa41a bugfixes in display of dynamicPicker 2022-01-26 12:08:18 +01:00
9056ea8449 styling changes collapse form 2022-01-26 12:08:18 +01:00
f0e349a2b0 show-hide files combined into one js file 2022-01-26 12:08:18 +01:00
2ccc6f1976 fix backend error of data transformer 2022-01-26 12:08:18 +01:00
cf041cf49e merge dynamic types 2022-01-26 12:08:18 +01:00
72d593e416 improve migration 2022-01-26 12:08:18 +01:00
287dca07aa php cs-fixes 2022-01-26 12:08:18 +01:00
910f9a7e8e update changelog 2022-01-26 12:08:18 +01:00
9457483a6b different dynamic entity picker types created 2022-01-26 12:08:18 +01:00
6d07168529 type variable added and file renamed in transformer 2022-01-26 12:08:18 +01:00
420f81ea24 fix phpstan error 2022-01-26 12:08:18 +01:00
416b62fc60 show hide functional 2022-01-26 12:08:18 +01:00
ecae7dab44 add placeholders for inputs 2022-01-26 12:07:39 +01:00
126bd1a4eb fix for comment embeddable null + template adjustments 2022-01-26 12:07:39 +01:00
450792e446 fix embeddableComment field, but still cannot be null and date+user_id are not set...? 2022-01-26 12:07:39 +01:00
ec155b1d67 bug fixed in entity 2022-01-26 12:07:39 +01:00
02b2af7d51 attempt to make show hide work 2022-01-26 12:07:39 +01:00
0a6a2c968c all views created for person resource 2022-01-26 12:07:39 +01:00
4b188e2df6 adjustments to form + rendering of resource kinds 2022-01-26 12:07:39 +01:00
d6bf1988f6 migration adjusted 2022-01-26 12:07:39 +01:00
94c9505c05 js files put in place + select menus added for selecting person/thirdparty/... show-hide not working yet 2022-01-26 12:07:39 +01:00
4a5a1440ff form created + general layout of templates 2022-01-26 12:06:44 +01:00
b31cc460fa route and menu entry added in person menu + start of templates/controller 2022-01-26 12:06:44 +01:00
48c3432191 changelog entry added 2022-01-26 12:06:44 +01:00
7b4405f6fe entities + migration created 2022-01-26 12:04:16 +01:00
ce3a74326d cs-fixes 2022-01-26 12:01:14 +01:00
51ab013bd7 violation added if all three association entities are null 2022-01-26 11:20:47 +01:00
41a6366efe vue homepage_widget: improve translations, titles, tabs order, add no data statement 2022-01-26 10:13:58 +01:00
c3c2fd30f0 better title proportions 2022-01-25 21:42:04 +01:00
1952e4aa5a render table results for works tab 2022-01-25 21:39:20 +01:00
1e1311b7c8 add counter pills in tabs 2022-01-25 21:09:11 +01:00
fc1ed8b71e display table with notifications datas, prepare others tabs to render datas 2022-01-25 20:08:39 +01:00
2144b247b3 store all makeFetch, with isLoaded check 2022-01-25 18:36:54 +01:00
e4629ed599 store first makeFetch, with loading spinner 2022-01-25 17:40:08 +01:00
f27dca6fb5 CSfixer changes 2022-01-25 16:41:44 +01:00
8c27ec0720 Fix user/thirdparty/freetext field in edit form 2022-01-25 16:38:37 +01:00
8392e2e6bd reorganize templates to fix edit form 2022-01-25 16:09:11 +01:00
963b0b1ed2 bugfixes in display of dynamicPicker 2022-01-25 16:08:40 +01:00
Pol Dellaiera
7513187a6d Revert "fix cs"
This reverts commit 7b3e9cb490.
2022-01-25 15:54:20 +01:00
Pol Dellaiera
990fc0e484 Remove obsolete file. 2022-01-25 15:50:07 +01:00
8fce27a128 move styles in better place 2022-01-25 15:43:45 +01:00
55c2c41cea styling changes collapse form 2022-01-25 14:46:09 +01:00
0bd10b6dfa show-hide files combined into one js file 2022-01-25 14:45:38 +01:00
8cd01d4317 fix backend error of data transformer 2022-01-25 14:43:51 +01:00
71293e3378 Merge branch '397_homepage' into homepage/rewrite 2022-01-25 14:41:53 +01:00
cb58afa44a Merge branch 'master' into homepage/rewrite 2022-01-25 14:40:38 +01:00
7b3e9cb490 fix cs 2022-01-25 14:40:28 +01:00
fb720a382f sticky buttons smaller 2022-01-25 14:32:57 +01:00
a4bfa1cb16 Merge remote-tracking branch 'origin/master' into homepage/rewrite 2022-01-25 14:28:29 +01:00
502f2aceed init sub-components and active tab mechanism 2022-01-25 13:02:24 +01:00
Pol Dellaiera
eb9cc0b63f Update minor things. 2022-01-25 12:19:14 +01:00
Pol Dellaiera
7ee554b48d Update minor things. 2022-01-25 11:56:30 +01:00
Pol Dellaiera
2e6d281bfc cs: Autofix coding standard based on updated rules. 2022-01-25 11:46:29 +01:00
Pol Dellaiera
4b1bf7ce23 Remove obsolete file. 2022-01-25 11:33:14 +01:00
Pol Dellaiera
2d574fea2e chore: Remove duplicate entries from composer.json and normalize it. 2022-01-25 11:29:59 +01:00
0e2772336f create new app vue homepage_widget, with store 2022-01-25 11:27:31 +01:00
320d11671a prepare homepage_widget template 2022-01-25 10:54:08 +01:00
dec26e2ba4 merge dynamic types 2022-01-24 19:42:06 +01:00
514d75fa5f improve migration 2022-01-24 19:26:16 +01:00
0ac51da31a Merge remote-tracking branch 'origin/issue386_dynamic_type_picker' into issue382_person_resource 2022-01-24 19:23:24 +01:00
90d43b0bd3 Merge remote-tracking branch 'origin/master' into issue382_person_resource 2022-01-24 19:21:06 +01:00
nobohan
25ee11e4ff upd CHANGELOG 2022-01-24 13:48:47 +01:00
nobohan
df661fc7c4 address: render blur address if confidential in address render box (twig) 2022-01-24 12:13:34 +01:00
nobohan
6b78c06dfb address: use address.confidential in AddressRenderBox.vue 2022-01-24 12:04:23 +01:00
nobohan
d087f051f0 address: remove maxlenght constraint in vuejs form for corridor, flat, steps, floor 2022-01-24 11:08:25 +01:00
nobohan
b3edba3abe address: add confidential in vuejs edit form 2022-01-24 11:04:17 +01:00
nobohan
02d8ceba25 address: add confidential in normalizer + code style fix 2022-01-24 10:42:05 +01:00
nobohan
741043c177 address: alter some address field to TEXT + add confidential field 2022-01-24 10:05:47 +01:00
c42f62de4c fix phpstan errors: undefined variable or type invalid 2022-01-24 00:32:47 +01:00
e23ef35b75 list of unread notificaiton api endpoint 2022-01-24 00:24:20 +01:00
eccc75aecf fix cs 2022-01-23 23:22:04 +01:00
7e2fbf93f9 task: api endpoint for my tasks 2022-01-23 23:13:25 +01:00
6ab8f95f7d list of "my evaluation near end" 2022-01-23 22:40:17 +01:00
1faa9812db fix variable name in work list page 2022-01-23 22:24:48 +01:00
a786578cbe fix cs 2022-01-23 21:52:27 +01:00
c08bd76e4b adding parameter until and since on 'work near end' 2022-01-23 21:52:16 +01:00
20a4950c60 accompanying period work list for my, near end date 2022-01-23 13:23:50 +01:00
92788ccdc0 php cs-fixes 2022-01-21 15:58:55 +01:00
84993ca05b update changelog 2022-01-21 15:55:22 +01:00
972657b38e different dynamic entity picker types created 2022-01-21 15:53:52 +01:00
b26d0905a3 type variable added and file renamed in transformer 2022-01-21 15:01:12 +01:00
4ab0a28f7d fix phpstan error 2022-01-21 13:05:38 +01:00
52009bd7c6 show hide functional 2022-01-21 12:35:33 +01:00
7acd6c7fc3 add placeholders for inputs 2022-01-21 12:34:14 +01:00
f26cba415b fix for comment embeddable null + template adjustments 2022-01-21 12:08:41 +01:00
8a2e588190 fix embeddableComment field, but still cannot be null and date+user_id are not set...? 2022-01-20 17:14:14 +01:00
d63d9c2f4e bug fixed in entity 2022-01-20 17:13:28 +01:00
dcdee1d6e3 attempt to make show hide work 2022-01-20 17:12:18 +01:00
dd589af1be all views created for person resource 2022-01-20 17:11:51 +01:00
2bdf116698 adjustments to form + rendering of resource kinds 2022-01-19 19:46:01 +01:00
96dc711b4f migration adjusted 2022-01-19 19:38:57 +01:00
001e2f9622 js files put in place + select menus added for selecting person/thirdparty/... show-hide not working yet 2022-01-19 09:48:39 +01:00
58ac4b01ef form created + general layout of templates 2022-01-18 14:54:03 +01:00
6e4bfd45c6 route and menu entry added in person menu + start of templates/controller 2022-01-18 13:55:09 +01:00
ff0b0678a5 changelog entry added 2022-01-18 11:11:29 +01:00
3b56cc818b entities + migration created 2022-01-18 11:08:24 +01:00
230 changed files with 6657 additions and 2226 deletions

View File

@@ -11,9 +11,48 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
* renomme date de début en date d'ouverture dans le formulaire parcours
## Test releases
### test release 2021-01-31
[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412)
* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413)
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household
* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal
### test release 2021-01-28
* [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362)
* [activity] Order activity by date and by id (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/364)
* [main] increase length of 4 Address fields (change to TEXT, no size limits) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/277)
* [main] Add confidential option for address, in edit and view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/165)
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [person] Add residential address entity, form and list for each person
* [aside_activity]: dynamicUserPickerType used (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/399)
* dispatching list
### test release 2021-01-26
* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406)
* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382)
* [person ressources]: module added
### test release 2022-01-24
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
@@ -32,7 +71,6 @@ and this project adheres to
* add My workflow section with my opened subscriptions
* apply workflow on documents, accompanyingCourseWork and Evaluations
* [wopi-link] a new vue component allow to open wopi link in a fullscreen chill-themed modal
<!-- end -->
### test release 2022-01-19
* vuejs: add dead information on all on-the-fly person render boxes, in vis graph and other templates (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/271)
@@ -40,6 +78,7 @@ and this project adheres to
* [main] location form type: fix unmapped address field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/246)
* [activity] fix wrong import of js assets for adding and viewing documents in activity (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/83 & https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/176)
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
* [forms] dynamic picker types for user/person/thirdparty types created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/386)
### test release 2022-01-17
@@ -49,10 +88,14 @@ and this project adheres to
* [main] Add mainLocation field to User entity and add it in user form type
* [course list in person context] show full username/label for ref
* [accompanying period work] remove the possibility to generate document from an accompanying period work
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
## Test releases
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
### test release 2022-01-12

View File

@@ -52,9 +52,6 @@
"twig/string-extra": "^3.3",
"twig/twig": "^3.0"
},
"conflict": {
"symfony/symfony": "*"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"drupol/php-conventions": "^5",
@@ -71,23 +68,17 @@
"symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4"
},
"config": {
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"vendor-dir": "tests/app/vendor",
"allow-plugins": {
"composer/package-versions-deprecated": true,
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"phpro/grumphp": true
}
"conflict": {
"symfony/symfony": "*"
},
"autoload": {
"psr-4": {
"Chill\\ActivityBundle\\": "src/Bundle/ChillActivityBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\BudgetBundle\\": "src/Bundle/ChillBudgetBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\CustomFieldsBundle\\": "src/Bundle/ChillCustomFieldsBundle",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
@@ -96,9 +87,6 @@
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
"Chill\\AsideActivityBundle\\": "src/Bundle/ChillAsideActivityBundle/src",
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\CalendarBundle\\": "src/Bundle/ChillCalendarBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
}
},
@@ -111,10 +99,10 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"ocramius/package-versions": true,
"phpro/grumphp": true,
"ocramius/package-versions": true
"phpstan/extension-installer": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
@@ -123,8 +111,8 @@
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"cache:clear": "symfony-cmd"
}
}
}

View File

@@ -1,3 +0,0 @@
add npm/yarn dependency in package.json :
"select2-bootstrap-theme": "0.1.0-beta.10",

View File

@@ -80,11 +80,6 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Foreach overwrites \\$action with its value variable\\.$#"
count: 1

View File

@@ -30,36 +30,6 @@ parameters:
count: 2
path: src/Bundle/ChillPersonBundle/Household/MembersEditorFactory.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Parameter \\$action of method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:countBySocialActionWithDescendants\\(\\) has invalid type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\SocialAction\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$action$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$limit$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$offset$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Undefined variable\\: \\$orderBy$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Variable variables are not allowed\\.$#"
count: 4

View File

@@ -400,11 +400,6 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Form/Type/PersonPhoneType.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\AccompanyingPeriodWorkRepository\\:\\:buildQueryBySocialActionWithDescendants\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Repository\\\\AccompanyingPeriod\\\\QueryBuilder\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 3

View File

@@ -256,7 +256,7 @@ final class ActivityController extends AbstractController
if ($person instanceof Person) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
$activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null);
->findByPerson($person, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
$event = new PrivacyEvent($person, [
'element_class' => Activity::class,
@@ -269,7 +269,7 @@ final class ActivityController extends AbstractController
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
$activities = $this->activityACLAwareRepository
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE);
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
}

View File

@@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Chill\ActivityBundle\Validator\Constraints as ActivityValidator;
use Chill\DocStoreBundle\Entity\Document;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
@@ -62,7 +61,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
* @Groups({"read", "docgen:read"})
* @Groups({"read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
@@ -76,16 +75,19 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence")
* @Groups({"docgen:read"})
*/
private ?ActivityPresence $attendee = null;
/**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="comment_")
* @Groups({"docgen:read"})
*/
private CommentEmbeddable $comment;
/**
* @ORM\Column(type="datetime")
* @Groups({"docgen:read"})
*/
private DateTime $date;
@@ -101,6 +103,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\Column(type="boolean", options={"default": false})
* @Groups({"docgen:read"})
*/
private bool $emergency = false;
@@ -131,16 +134,19 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\ActivityBundle\Entity\ActivityReason")
* @Groups({"docgen:read"})
*/
private Collection $reasons;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
* @Groups({"docgen:read"})
*/
private ?Scope $scope = null;
/**
* @ORM\Column(type="string", options={"default": ""})
* @Groups({"docgen:read"})
*/
private string $sentReceived = '';
@@ -171,12 +177,13 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"docgen:read"})
*/
private User $user;
/**
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private ?Collection $users = null;
@@ -303,6 +310,18 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->documents;
}
/**
* @Groups({"docgen:read"})
*/
public function getDurationMinute(): int
{
if (null === $this->durationTime) {
return 0;
}
return (int) round(($this->durationTime->getTimestamp() + $this->durationTime->getOffset()) / 60.0, 0);
}
public function getDurationTime(): ?DateTime
{
return $this->durationTime;
@@ -403,6 +422,18 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->travelTime;
}
/**
* @Groups({"docgen:read"})
*/
public function getTravelTimeMinute(): int
{
if (null === $this->travelTime) {
return 0;
}
return (int) round(($this->travelTime->getTimestamp() + $this->travelTime->getOffset()) / 60.0, 0);
}
/**
* @deprecated
*/

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation as Serializer;
/**
* Class ActivityPresence.
@@ -31,11 +32,14 @@ class ActivityPresence
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Groups({"docgen:read"})
*/
private ?int $id;
/**
* @ORM\Column(type="json")
* @Serializer\Groups({"docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $name = [];

View File

@@ -13,8 +13,10 @@ namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ActivityType.
@@ -118,6 +120,7 @@ class ActivityType
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"docgen:read"})
*/
private ?int $id;
@@ -133,7 +136,8 @@ class ActivityType
/**
* @ORM\Column(type="json")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $name = [];
@@ -190,7 +194,6 @@ class ActivityType
/**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
* @Assert\EqualTo(propertyPath="socialIssuesVisible", message="This parameter must be equal to social issue parameter")
*/
private int $socialActionsVisible = self::FIELD_INVISIBLE;
@@ -260,6 +263,23 @@ class ActivityType
*/
private int $userVisible = self::FIELD_REQUIRED;
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!($this->socialIssuesVisible === 2 && $this->socialActionsVisible === 1)) {
$context
->buildViolation('The socialActionsVisible value is not compatible with the socialIssuesVisible value')
->atPath('socialActionsVisible')
->addViolation();
}
}
}
/**
* Get active
* return true if the type is active.

View File

@@ -34,6 +34,8 @@ p.date-label {
font-size: 18pt;
}
div.dashboard,
h4.badge-title,
h3.badge-title,
h2.badge-title {
ul.list-content {
font-size: 70%;

View File

@@ -1,45 +1,48 @@
<template>
<teleport to="#add-persons" v-if="isComponentVisible">
<teleport to="#add-persons" v-if="isComponentVisible">
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key"
v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc>
</div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
<span>{{ p.text }}</span>
</li>
</ul>
</div>
<div class="flex-bloc concerned-groups" :class="getContext">
<persons-bloc
v-for="bloc in contextPersonsBlocs"
v-bind:key="bloc.key"
v-bind:bloc="bloc"
v-bind:blocWidth="getBlocWidth"
v-bind:setPersonsInBloc="setPersonsInBloc">
</persons-bloc>
</div>
<div v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0">
<ul class="list-suggest add-items inline">
<li v-for="(p, i) in suggestedEntities" @click="addSuggestedEntity(p)" :key="`suggestedEntities-${i}`">
<person-text v-if="p.type === 'person'" :person="p"></person-text>
<span v-else>{{ p.text }}</span>
</li>
</ul>
</div>
<add-persons
buttonTitle="activity.add_persons"
modalTitle="activity.add_persons"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons">
</add-persons>
<add-persons
buttonTitle="activity.add_persons"
modalTitle="activity.add_persons"
v-bind:key="addPersons.key"
v-bind:options="addPersonsOptions"
@addNewPersons="addNewPersons"
ref="addPersons">
</add-persons>
</teleport>
</teleport>
</template>
<script>
import { mapState, mapGetters } from 'vuex';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import PersonsBloc from './ConcernedGroups/PersonsBloc.vue';
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default {
name: "ConcernedGroups",
components: {
AddPersons,
PersonsBloc
PersonsBloc,
PersonText
},
data() {
return {

View File

@@ -1,21 +1,29 @@
<template>
<li>
<span :title="person.text">
<span class="chill_denomination" @click.prevent="$emit('remove', person)">{{ textCutted }}</span>
<span class="chill_denomination" @click.prevent="$emit('remove', person)">
<person-text :person="person" :isCut="true"></person-text>
</span>
</span>
</li>
</template>
<script>
import PersonText from 'ChillPersonAssets/vuejs/_components/Entity/PersonText.vue';
export default {
name: "PersonBadge",
props: ['person'],
computed: {
textCutted() {
let more = (this.person.text.length > 15) ?'…' : '';
return this.person.text.slice(0,15) + more;
}
components: {
PersonText
},
// computed: {
// textCutted() {
// let more = (this.person.text.length > 15) ?'…' : '';
// return this.person.text.slice(0,15) + more;
// }
// },
emits: ['remove'],
}
</script>

View File

@@ -41,6 +41,17 @@
</div>
{% endif %}
{% if activity.attendee and t.attendeeVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Attendee'|trans }}</h3></div>
<div class="wl-col list">
<p class="wl-item">
{{ activity.attendee.name|localize_translatable_string }}
</p>
</div>
</div>
{% endif %}
{% if activity.sentReceived is not empty and t.sentReceivedVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Sent received'|trans }}</h3></div>

View File

@@ -2,10 +2,12 @@
{% if is_granted('CHILL_ACTIVITY_SEE_DETAILS', activity) %}
{% if no_action is not defined or no_action == false %}
<li>
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_main_notification_create', {
'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity',
'entityId': activity.id
}) }}">{{ 'notification.Notify'|trans }}</a>
}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
{{ 'notification.Notify'|trans }}</a>
</li>
{% endif %}
{% if context == 'person' and activity.accompanyingPeriod is not empty %}

View File

@@ -48,7 +48,7 @@
<li class="associated-persons">
<span class="item-key">{{ 'Participants'|trans ~ ' : ' }}</span>
{% for p in activity.personsAssociated %}
<span class="badge-person">{{ p|chill_entity_render_box }}</span>
<span class="badge-person">{{ p|chill_entity_render_box({'addAgeBadge': true}) }}</span>
{% endfor %}
</li>
</ul>

View File

@@ -165,11 +165,7 @@
<dt class="inline">{{ 'Attendee'|trans }}</dt>
<dd>
{% if entity.attendee is not null %}
{% if entity.attendee %}
{{ 'present'|trans|capitalize }}
{% else %}
{{ 'not present'|trans|capitalize }}
{% endif %}
{{ entity.attendee.name|localize_translatable_string }}
{% else %}
<span class="chill-no-data-statement">{{ 'None'|trans|capitalize }}</span>
{% endif %}
@@ -181,6 +177,13 @@
</div>
</div>
<div class="notification notification-list">
{% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %}
{% if notifications is not empty %}
{{ notifications|raw }}
{% endif %}
</div>
{% set person_id = null %}
{% if person %}
{% set person_id = person.id %}
@@ -197,18 +200,21 @@
{{ 'Back to the list'|trans }}
</a>
</li>
{% if is_granted('CHILL_ACTIVITY_UPDATE', entity) %}
<li>
<a class="btn btn-update" href="{{ path('chill_activity_activity_edit', { 'id': entity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}">
{{ 'Edit'|trans }}
<li>
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
{{ 'notification.Notify'|trans }}
</a>
</li>
{% if is_granted('CHILL_ACTIVITY_UPDATE', entity) %}
<li>
<a href="{{ path('chill_activity_activity_edit', { 'id': entity.id, 'person_id': person_id, 'accompanying_period_id': accompanying_course_id }) }}"
class="btn btn-update">{{ 'Edit'|trans }}</a>
</li>
{% endif %}
{% if is_granted('CHILL_ACTIVITY_DELETE', entity) %}
<li>
<a href="{{ path('chill_activity_activity_delete', { 'id': entity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}" class="btn btn-delete">
{{ 'Delete'|trans }}
</a>
<a href="{{ path('chill_activity_activity_delete', { 'id': entity.id, 'person_id' : person_id, 'accompanying_period_id': accompanying_course_id } ) }}"
class="btn btn-delete" title="{{ 'Delete'|trans }}"></a>
</li>
{% endif %}
</ul>

View File

@@ -25,19 +25,5 @@
{% endblock content %}
{% block block_post_menu %}
<div class="post-menu pt-4">
<div class="d-grid gap-2">
<a class="btn btn-primary" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
{{ 'notification.Notify'|trans }}
</a>
</div>
{% set notifications = chill_list_notifications('Chill\\ActivityBundle\\Entity\\Activity', entity.id) %}
{% if notifications is not empty %}
{{ notifications|raw }}
{% endif %}
</div>
<div class="post-menu pt-4"></div>
{% endblock %}

View File

@@ -28,8 +28,7 @@
<div class="post-menu pt-4">
<div class="d-grid gap-2">
<a class="btn btn-primary" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
<i class="fa fa-paper-plane fa-fw"></i>
<a class="btn btn-notify" href="{{ chill_path_add_return_path('chill_main_notification_create', {'entityClass': 'Chill\\ActivityBundle\\Entity\\Activity', 'entityId': entity.id}) }}">
{{ 'notification.Notify'|trans }}
</a>
</div>

View File

@@ -24,7 +24,6 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@@ -33,8 +32,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
class ActivityContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator;
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator;
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Aggregator;
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Filter;
namespace Chill\ActivityBundle\Tests\Export\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use Doctrine\Common\Collections\ArrayCollection;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\ActivityBundle\Tests\Filter;
namespace Chill\ActivityBundle\Tests\Export\Filter;
use Chill\MainBundle\Test\Export\AbstractFilterTest;
use DateTime;

View File

@@ -29,9 +29,13 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
final class ActivityVoterTest extends KernelTestCase
{
use PrepareActivityTrait;
use PrepareCenterTrait;
use PreparePersonTrait;
use PrepareScopeTrait;
use PrepareUserTrait;
/**

View File

@@ -20,3 +20,4 @@ For this type of activity, you must add at least one social action: Pour ce type
# admin
This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales"
The socialActionsVisible value is not compatible with the socialIssuesVisible value: Cette valeur du paramètre "Visibilité du champs Actions sociales" n'est pas compatible avec la valeur du paramètre "Visibilité du champs Problématiques sociales"

View File

@@ -32,6 +32,8 @@ final class AsideActivityController extends CRUDController
{
$asideActivity = new AsideActivity();
$asideActivity->setAgent($this->getUser());
$duration = $request->query->get('duration', '300');
$duration = DateTime::createFromFormat('U', $duration);
$asideActivity->setDuration($duration);

View File

@@ -14,9 +14,9 @@ namespace Chill\AsideActivityBundle\Form;
use Chill\AsideActivityBundle\Entity\AsideActivity;
use Chill\AsideActivityBundle\Entity\AsideActivityCategory;
use Chill\AsideActivityBundle\Templating\Entity\CategoryRender;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use DateInterval;
use DateTime;
use DateTimeImmutable;
@@ -68,22 +68,10 @@ final class AsideActivityFormType extends AbstractType
];
$builder
->add(
'agent',
EntityType::class,
[
'label' => 'For agent',
'required' => true,
'class' => User::class,
'data' => $this->storage->getToken()->getUser(),
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('u')->where('u.enabled = true');
},
'attr' => ['class' => 'select2 '],
'placeholder' => 'Choose the agent for whom this activity is created',
'choice_label' => 'username',
]
)
->add('agent', PickUserDynamicType::class, [
'label' => 'For agent',
'required' => true,
])
->add(
'date',
ChillDateType::class,

View File

@@ -1,5 +1,15 @@
{% extends '@ChillMain/Admin/layout.html.twig' %}
{% block js %}
{{ parent() }}
{{ encore_entry_script_tags('mod_pickentity_type') }}
{% endblock %}
{% block css %}
{{ parent() }}
{{ encore_entry_link_tags('mod_pickentity_type') }}
{% endblock %}
{% block title %}
{% include('@ChillMain/CRUD/_new_title.html.twig') %}
{% endblock %}
@@ -8,4 +18,5 @@
{% embed '@ChillMain/CRUD/_new_content.html.twig' %}
{% block content_form_actions_save_and_show %}{% endblock %}
{% endembed %}
{% endblock %}
{% endblock %}

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests;
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldChoice;
use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests;
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldNumber;
use Chill\CustomFieldsBundle\Entity\CustomField;

View File

@@ -9,10 +9,11 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests;
namespace Chill\CustomFieldsBundle\Tests\CustomFields;
use Chill\CustomFieldsBundle\CustomFields\CustomFieldText;
use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\CustomFieldsBundle\Tests\CustomFieldTestHelper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**

View File

@@ -9,7 +9,7 @@
declare(strict_types=1);
namespace Chill\CustomFieldsBundle\Tests;
namespace Chill\CustomFieldsBundle\Tests\Routing;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

View File

@@ -17,7 +17,7 @@
<select class="form-select" v-model="template">
<option disabled selected value="">{{ $t('choose_a_template') }}</option>
<template v-for="t in templates">
<option v-bind:value="t.id">{{ t.name.fr }}</option>
<option v-bind:value="t.id">{{ t.name.fr || 'Aucun nom défini' }}</option>
</template>
</select>
<button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button>
@@ -48,6 +48,7 @@ export default {
required: true,
},
templates: {
type: Array,
required: true,
},
// beforeMove execute "something" before
@@ -73,7 +74,11 @@ export default {
return true;
},
getDescription() {
return this.templates.find(t => t.id === this.template).description || '';
let desc = this.templates.find(t => t.id === this.template);
if (null === desc) {
return '';
}
return desc.description || '';
},
},
methods: {

View File

@@ -21,24 +21,28 @@
</div>
</div>
{% set freezed = false %}
{% for step in entity_workflow.stepsChained %}
{% if loop.last %}
{% if step.previous is not null and step.previous.freezeAfter == true %}
{% set freezed = true %}
{% endif %}
{% endif %}
{% endfor %}
{% if display_action is defined and display_action == true %}
<ul class="record_actions">
<li>
{{ m.download_button(document.object, document.title) }}
</li>
<li>
{#
data-button is optional !
OPTIONS:
'changeIcon' string
{% if not freezed %}
{% set button = {
'changeIcon': 'fa-unlock',
} %}{#
'changeClass' string
'noText' boolean
#}{% set button = {
'changeIcon': 'fa-unlock',
} %}
#}
{# vue component #}
<span
data-module="wopi-link"
@@ -47,6 +51,11 @@
data-doc-type="{{ document.object.type|e('html_attr') }}"
data-button="{{ button|json_encode }}"
></span>
{% else %}
<a class="btn btn-update change-icon disabled" href="#" title="{{ 'workflow.freezed document'|trans }}">
<i class="fa fa-lock me-2"></i>{{ 'Update document'|trans }}
</a>
{% endif %}
</li>
</ul>
{% endif %}

View File

@@ -56,17 +56,18 @@
</a>
</li>
{% endif %}
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
{% if workflows_frame is not empty %}
<li>
{{ workflows_frame|raw }}
</li>
{% endif %}
</ul>
</div>
{% endblock %}
{% block block_post_menu %}
<div class="post-menu pt-4">
{% set workflows_frame = chill_entity_workflow_list('Chill\\DocStoreBundle\\Entity\\AccompanyingCourseDocument', document.id) %}
{% if workflows_frame is not empty %}
{{ workflows_frame|raw }}
{% endif %}
</div>
<div class="post-menu pt-4"></div>
{% endblock %}
{% block js %}

View File

@@ -13,13 +13,19 @@ namespace Chill\MainBundle\Controller;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\NotificationRepository;
use Chill\MainBundle\Security\Authorization\NotificationVoter;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\MainBundle\Serializer\Model\Counter;
use Doctrine\ORM\EntityManagerInterface;
use RuntimeException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\SerializerInterface;
use UnexpectedValueException;
/**
@@ -29,12 +35,26 @@ class NotificationApiController
{
private EntityManagerInterface $entityManager;
private NotificationRepository $notificationRepository;
private PaginatorFactory $paginatorFactory;
private Security $security;
public function __construct(EntityManagerInterface $entityManager, Security $security)
{
private SerializerInterface $serializer;
public function __construct(
EntityManagerInterface $entityManager,
NotificationRepository $notificationRepository,
PaginatorFactory $paginatorFactory,
Security $security,
SerializerInterface $serializer
) {
$this->entityManager = $entityManager;
$this->notificationRepository = $notificationRepository;
$this->paginatorFactory = $paginatorFactory;
$this->security = $security;
$this->serializer = $serializer;
}
/**
@@ -53,6 +73,37 @@ class NotificationApiController
return $this->markAs('unread', $notification);
}
/**
* @Route("/my/unread")
*/
public function myUnreadNotifications(Request $request): JsonResponse
{
$total = $this->notificationRepository->countUnreadByUser($this->security->getUser());
if ($request->query->getBoolean('countOnly')) {
return new JsonResponse(
$this->serializer->serialize(new Counter($total), 'json', ['groups' => ['read']]),
JsonResponse::HTTP_OK,
[],
true
);
}
$paginator = $this->paginatorFactory->create($total);
$notifications = $this->notificationRepository->findUnreadByUser(
$this->security->getUser(),
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
$collection = new Collection($notifications, $paginator);
return new JsonResponse(
$this->serializer->serialize($collection, 'json', ['groups' => ['read']]),
JsonResponse::HTTP_OK,
[],
true
);
}
private function markAs(string $target, Notification $notification): JsonResponse
{
if (!$this->security->isGranted(NotificationVoter::NOTIFICATION_TOGGLE_READ_STATUS, $notification)) {

View File

@@ -41,6 +41,11 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->scalarNode('phonenumber_default_country_code')
->cannotBeEmpty()
->isRequired()
->defaultValue('+32')
->end() // end of scalar 'phonenumber_default_country_code'
->scalarNode('installation_name')
->cannotBeEmpty()
->defaultValue('Chill')

View File

@@ -42,10 +42,16 @@ class Address
*/
private $buildingName;
/**
* @ORM\Column(type="boolean")
* @Groups({"write"})
*/
private bool $confidential = false;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"write"})
*/
private $corridor;
@@ -78,7 +84,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"write"})
*/
private $flat;
@@ -86,7 +92,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"write"})
*/
private $floor;
@@ -143,7 +149,7 @@ class Address
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
* @ORM\Column(type="string", length=255, nullable=true)
* @Groups({"write"})
*/
private $steps;
@@ -192,6 +198,7 @@ class Address
return (new Address())
->setAddressReference($original->getAddressReference())
->setBuildingName($original->getBuildingName())
->setConfidential($original->getConfidential())
->setCorridor($original->getCorridor())
->setCustoms($original->getCustoms())
->setDistribution($original->getDistribution())
@@ -229,6 +236,11 @@ class Address
return $this->buildingName;
}
public function getConfidential(): bool
{
return $this->confidential;
}
public function getCorridor(): ?string
{
return $this->corridor;
@@ -369,6 +381,13 @@ class Address
return $this;
}
public function setConfidential(bool $confidential): self
{
$this->confidential = $confidential;
return $this;
}
public function setCorridor(?string $corridor): self
{
$this->corridor = $corridor;

View File

@@ -20,10 +20,9 @@ use Doctrine\ORM\Mapping as ORM;
class CommentEmbeddable
{
/**
* @var string
* @ORM\Column(type="text", nullable=true)
*/
private $comment;
private ?string $comment = null;
/**
* @var DateTime
@@ -39,10 +38,7 @@ class CommentEmbeddable
*/
private $userId;
/**
* @return string
*/
public function getComment()
public function getComment(): ?string
{
return $this->comment;
}
@@ -68,9 +64,6 @@ class CommentEmbeddable
return empty($this->getComment());
}
/**
* @param string $comment
*/
public function setComment(?string $comment)
{
$this->comment = $comment;

View File

@@ -64,7 +64,7 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Groups({"read", "write"})
* @Serializer\Groups({"read", "write", "docgen:read"})
*/
private ?string $email = null;

View File

@@ -0,0 +1,166 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Entity;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Repository\ResidentialAddressRepository;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=ResidentialAddressRepository::class)
* @ORM\Table(name="chill_main_residential_address")
*/
class ResidentialAddress
{
/**
* @ORM\ManyToOne(targetEntity=Address::class)
* @ORM\JoinColumn(nullable=true)
*/
private ?Address $address = null;
/**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="residentialAddressComment_")
*/
private CommentEmbeddable $comment;
/**
* @ORM\Column(type="datetime_immutable", nullable=true)
*/
private ?DateTimeImmutable $endDate = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
* @ORM\JoinColumn(nullable=true)
*/
private ?Person $hostPerson = null;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @ORM\JoinColumn(nullable=true)
*/
private ?ThirdParty $hostThirdParty = null;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
* @ORM\JoinColumn(nullable=false)
*/
private Person $person;
/**
* @ORM\Column(type="datetime_immutable")
*/
private ?DateTimeImmutable $startDate = null;
public function __construct()
{
$this->comment = new CommentEmbeddable();
}
public function getAddress(): ?Address
{
return $this->address;
}
public function getComment(): CommentEmbeddable
{
return $this->comment;
}
public function getEndDate(): ?DateTimeImmutable
{
return $this->endDate;
}
public function getHostPerson(): ?Person
{
return $this->hostPerson;
}
public function getHostThirdParty(): ?ThirdParty
{
return $this->hostThirdParty;
}
public function getId(): ?int
{
return $this->id;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function getStartDate(): ?DateTimeImmutable
{
return $this->startDate;
}
public function setAddress(?Address $address): self
{
$this->address = $address;
return $this;
}
public function setComment(CommentEmbeddable $comment): self
{
$this->comment = $comment;
return $this;
}
public function setEndDate(?DateTimeImmutable $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function setHostPerson(?Person $hostPerson): self
{
$this->hostPerson = $hostPerson;
return $this;
}
public function setHostThirdParty(?ThirdParty $hostThirdParty): self
{
$this->hostThirdParty = $hostThirdParty;
return $this;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function setStartDate(DateTimeImmutable $startDate): self
{
$this->startDate = $startDate;
return $this;
}
}

View File

@@ -25,6 +25,7 @@ use Iterator;
use RuntimeException;
use Symfony\Component\Serializer\Annotation as Serializer;
use function count;
use function is_array;
/**
* @ORM\Entity
@@ -37,6 +38,7 @@ use function count;
class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**
@@ -50,7 +52,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
*/
private ?int $id = null;
@@ -72,6 +73,11 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
*/
private Collection $steps;
/**
* @var null|array|EntityWorkflowStep[]
*/
private ?array $stepsChainedCache = null;
/**
* @ORM\ManyToMany(targetEntity=User::class)
* @ORM\JoinTable(name="chill_main_workflow_entity_subscriber_to_final")
@@ -129,10 +135,6 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
if (!$this->steps->contains($step)) {
$this->steps[] = $step;
$step->setEntityWorkflow($this);
if ($this->isFinalize()) {
$step->setFinalizeAfter(true);
}
}
return $this;
@@ -253,27 +255,33 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
public function getStepsChained(): array
{
if (is_array($this->stepsChainedCache)) {
return $this->stepsChainedCache;
}
$iterator = $this->steps->getIterator();
$previous = $next = $current = null;
$current = null;
$steps = [];
$iterator->rewind();
while ($iterator->valid()) {
do {
$previous = $current;
$steps[] = $current = $iterator->current();
$current = $iterator->current();
$steps[] = $current;
$current->setPrevious($previous);
$iterator->next();
if ($iterator->valid()) {
$next = $iterator->current();
$current->setNext($iterator->current());
} else {
$next = null;
$current->setNext(null);
}
} while ($iterator->valid());
$current->setNext($next);
}
$this->stepsChainedCache = $steps;
return $steps;
}
@@ -308,7 +316,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
return $this->workflowName;
}
public function isFinalize(): bool
public function isFinal(): bool
{
$steps = $this->getStepsChained();
@@ -320,7 +328,7 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
/** @var EntityWorkflowStep $last */
$last = end($steps);
return $last->getPrevious()->isFinalizeAfter();
return $last->isFinal();
}
public function isFreeze(): bool

View File

@@ -24,6 +24,7 @@ use Doctrine\ORM\Mapping as ORM;
class EntityWorkflowComment implements TrackCreationInterface, TrackUpdateInterface
{
use TrackCreationTrait;
use TrackUpdateTrait;
/**

View File

@@ -53,11 +53,6 @@ class EntityWorkflowStep
*/
private ?EntityWorkflow $entityWorkflow = null;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
private bool $finalizeAfter = false;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
@@ -70,6 +65,11 @@ class EntityWorkflowStep
*/
private ?int $id = null;
/**
* @ORM\Column(type="boolean", options={"default": false})
*/
private bool $isFinal = false;
/**
* filled by @see{EntityWorkflow::getStepsChained}.
*/
@@ -187,9 +187,9 @@ class EntityWorkflowStep
return $this->transitionByEmail;
}
public function isFinalizeAfter(): bool
public function isFinal(): bool
{
return $this->finalizeAfter;
return $this->isFinal;
}
public function isFreezeAfter(): bool
@@ -244,16 +244,16 @@ class EntityWorkflowStep
return $this;
}
public function setFinalizeAfter(bool $finalizeAfter): EntityWorkflowStep
public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
{
$this->finalizeAfter = $finalizeAfter;
$this->freezeAfter = $freezeAfter;
return $this;
}
public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
public function setIsFinal(bool $isFinal): EntityWorkflowStep
{
$this->freezeAfter = $freezeAfter;
$this->isFinal = $isFinal;
return $this;
}

View File

@@ -12,14 +12,18 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form\Type\DataTransformer;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use UnexpectedValueException;
use function array_key_exists;
class UserToJsonTransformer implements DataTransformerInterface
class EntityToJsonTransformer implements DataTransformerInterface
{
private DenormalizerInterface $denormalizer;
@@ -27,11 +31,14 @@ class UserToJsonTransformer implements DataTransformerInterface
private SerializerInterface $serializer;
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, bool $multiple)
private string $type;
public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, bool $multiple, string $type)
{
$this->denormalizer = $denormalizer;
$this->serializer = $serializer;
$this->multiple = $multiple;
$this->type = $type;
}
public function reverseTransform($value)
@@ -49,6 +56,10 @@ class UserToJsonTransformer implements DataTransformerInterface
);
}
if ('' === $value) {
return null;
}
return $this->denormalizeOne($denormalized);
}
@@ -66,7 +77,7 @@ class UserToJsonTransformer implements DataTransformerInterface
]);
}
private function denormalizeOne(array $item): User
private function denormalizeOne(array $item)
{
if (!array_key_exists('type', $item)) {
throw new TransformationFailedException('the key "type" is missing on element');
@@ -76,10 +87,30 @@ class UserToJsonTransformer implements DataTransformerInterface
throw new TransformationFailedException('the key "id" is missing on element');
}
switch ($this->type) {
case 'user':
$class = User::class;
break;
case 'person':
$class = Person::class;
break;
case 'thirdparty':
$class = ThirdParty::class;
break;
default:
throw new UnexpectedValueException('This type is not supported');
}
return
$this->denormalizer->denormalize(
['type' => $item['type'], 'id' => $item['id']],
User::class,
$class,
'json',
[AbstractNormalizer::GROUPS => ['read']],
);

View File

@@ -12,7 +12,7 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\DataTransformer\UserToJsonTransformer;
use Chill\MainBundle\Form\Type\DataTransformer\EntityToJsonTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
@@ -38,7 +38,7 @@ class PickUserDynamicType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer(new UserToJsonTransformer($this->denormalizer, $this->serializer, $options['multiple']));
$builder->addViewTransformer(new EntityToJsonTransformer($this->denormalizer, $this->serializer, $options['multiple'], 'user'));
}
public function buildView(FormView $view, FormInterface $form, array $options)
@@ -58,6 +58,6 @@ class PickUserDynamicType extends AbstractType
public function getBlockPrefix()
{
return 'pick_user_dynamic';
return 'pick_entity_dynamic';
}
}

View File

@@ -0,0 +1,73 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Form\Type;
use Chill\MainBundle\Entity\ResidentialAddress;
use Chill\PersonBundle\Form\Type\PickPersonDynamicType;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class ResidentialAddressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('startDate', DateType::class, [
'required' => true,
'input' => 'datetime_immutable',
'widget' => 'single_text',
])
->add('endDate', DateType::class, [
'required' => false,
'input' => 'datetime_immutable',
'widget' => 'single_text',
])
->add('comment', CommentType::class, [
'required' => false,
]);
if ('person' === $options['kind']) {
$builder
->add('hostPerson', PickPersonDynamicType::class, [
'label' => 'Person',
]);
}
if ('thirdparty' === $options['kind']) {
$builder
->add('hostThirdParty', PickThirdpartyDynamicType::class, [
'label' => 'Third party',
]);
}
if ('address' === $options['kind']) {
$builder
->add('address', PickAddressType::class, [
'required' => false,
'label' => 'Address',
'use_valid_from' => false,
'use_valid_to' => false,
]);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ResidentialAddress::class,
'kind' => null,
]);
}
}

View File

@@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use LogicException;
use Symfony\Component\Form\AbstractType;
@@ -24,6 +25,7 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Transition;
use function array_key_exists;
class WorkflowStepType extends AbstractType
{
@@ -31,10 +33,13 @@ class WorkflowStepType extends AbstractType
private Registry $registry;
public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry)
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(EntityWorkflowManager $entityWorkflowManager, Registry $registry, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->entityWorkflowManager = $entityWorkflowManager;
$this->registry = $registry;
$this->translatableStringHelper = $translatableStringHelper;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -42,6 +47,7 @@ class WorkflowStepType extends AbstractType
/** @var \Chill\MainBundle\Entity\Workflow\EntityWorkflow $entityWorkflow */
$entityWorkflow = $options['entity_workflow'];
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
if (true === $options['transition']) {
if (null === $options['entity_workflow']) {
@@ -53,20 +59,49 @@ class WorkflowStepType extends AbstractType
->getEnabledTransitions($entityWorkflow);
$choices = array_combine(
array_map(static function (Transition $transition) { return $transition->getName(); }, $transitions),
array_map(
static function (Transition $transition) {
return $transition->getName();
},
$transitions
),
$transitions
);
$builder
->add('transition', ChoiceType::class, [
'label' => 'workflow.Transition',
'label' => 'workflow.Transition to apply',
'mapped' => false,
'multiple' => false,
'expanded' => true,
'choices' => $choices,
'choice_label' => static function (Transition $transition) {
return implode(', ', $transition->getTos());
},
'choice_label' => function (Transition $transition) use ($workflow) {
$meta = $workflow->getMetadataStore()->getTransitionMetadata($transition);
if (array_key_exists('label', $meta)) {
return $this->translatableStringHelper->localize($meta['label']);
}
return $transition->getName();
},
'choice_attr' => static function (Transition $transition) use ($workflow) {
$toFinal = true;
foreach ($transition->getTos() as $to) {
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
if (
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
) {
$toFinal = false;
}
}
return [
'data-is-transition' => 'data-is-transition',
'data-to-final' => $toFinal ? '1' : '0',
];
},
])
->add('future_dest_users', PickUserDynamicType::class, [
'label' => 'workflow.dest for next steps',
@@ -88,11 +123,6 @@ class WorkflowStepType extends AbstractType
}
$builder
->add('finalizeAfter', CheckboxType::class, [
'required' => false,
'label' => 'workflow.Finalize',
'help' => 'workflow.The workflow will be finalized',
])
->add('comment', ChillTextareaType::class, [
'required' => false,
'label' => 'Comment',

View File

@@ -11,17 +11,27 @@ declare(strict_types=1);
namespace Chill\MainBundle\Notification\Templating;
use Chill\MainBundle\Entity\NotificationComment;
use Chill\MainBundle\Form\NotificationCommentType;
use Chill\MainBundle\Notification\NotificationPresence;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Twig\Environment;
use Twig\Extension\RuntimeExtensionInterface;
class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
{
private FormFactoryInterface $formFactory;
private NotificationPresence $notificationPresence;
public function __construct(NotificationPresence $notificationPresence)
private UrlGeneratorInterface $urlGenerator;
public function __construct(FormFactoryInterface $formFactory, NotificationPresence $notificationPresence, UrlGeneratorInterface $urlGenerator)
{
$this->formFactory = $formFactory;
$this->notificationPresence = $notificationPresence;
$this->urlGenerator = $urlGenerator;
}
public function counterNotificationFor(Environment $environment, string $relatedEntityClass, int $relatedEntityId, array $options = []): string
@@ -47,8 +57,24 @@ class NotificationTwigExtensionRuntime implements RuntimeExtensionInterface
return '';
}
$appendCommentForms = [];
foreach ($notifications as $notification) {
$appendComment = new NotificationComment();
$appendCommentForms[$notification->getId()] = $this->formFactory->create(
NotificationCommentType::class,
$appendComment,
[
'action' => $this->urlGenerator->generate(
'chill_main_notification_show',
['id' => $notification->getId()]
),
]
)->createView();
}
return $environment->render('@ChillMain/Notification/extension_list_notifications_for.html.twig', [
'notifications' => $notifications,
'notifications' => $notifications, 'appendCommentForms' => $appendCommentForms,
]);
}
}

View File

@@ -193,6 +193,29 @@ final class NotificationRepository implements ObjectRepository
return $this->repository->findOneBy($criteria, $orderBy);
}
/**
* @return array|Notification[]
*/
public function findUnreadByUser(User $user, int $limit = 20, int $offset = 0): array
{
$rsm = new Query\ResultSetMappingBuilder($this->em);
$rsm->addRootEntityFromClassMetadata(Notification::class, 'cmn');
$sql = 'SELECT ' . $rsm->generateSelectClause(['cmn' => 'cmn']) . ' ' .
'FROM chill_main_notification cmn ' .
'WHERE ' .
'EXISTS (select 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId and cmnau.notification_id = cmn.id) ' .
'ORDER BY cmn.date DESC ' .
'LIMIT :limit OFFSET :offset';
$nq = $this->em->createNativeQuery($sql, $rsm)
->setParameter('userId', $user->getId())
->setParameter('limit', $limit)
->setParameter('offset', $offset);
return $nq->getResult();
}
public function getClassName()
{
return Notification::class;

View File

@@ -0,0 +1,59 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\ResidentialAddress;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ResidentialAddress|null find($id, $lockMode = null, $lockVersion = null)
* @method ResidentialAddress|null findOneBy(array $criteria, array $orderBy = null)
* @method ResidentialAddress[] findAll()
* @method ResidentialAddress[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ResidentialAddressRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ResidentialAddress::class);
}
// /**
// * @return ResidentialAddress[] Returns an array of ResidentialAddress objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->orderBy('r.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?ResidentialAddress
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@@ -474,6 +474,7 @@ div.workflow {
// Override bootstrap popover styles
div.popover {
box-shadow: 0 0 10px -5px $dark;
z-index: 9999;
.popover-arrow {}
.popover-header {}
.popover-body {}

View File

@@ -12,6 +12,7 @@
display: block;
top: calc(50% - 7px);
right: 10px;
line-height: 11px;
}
}
@@ -62,14 +63,19 @@ ul.list-suggest {
& span:hover {
color: $chill-l-gray;
}
.person-text {
span {
padding-left: 0px;
}
}
}
}
&.remove-items {
li {
position: relative;
span {
& > span {
display: block;
padding-right: .75rem;
padding-right: 1.75rem;
@include remove_link;
}
}

View File

@@ -21,7 +21,8 @@ $chill-theme-buttons: (
"download": $gray-300,
"cancel": $gray-300,
"choose": $gray-300,
"notify": $gray-300,
"notify": $chill-blue,
"search": $gray-300,
"unlink": $chill-red,
"tpchild": $chill-pink,
);
@@ -80,6 +81,7 @@ $chill-theme-buttons: (
&.btn-notify::before,
&.btn-tpchild::before,
&.btn-download::before,
&.btn-search::before,
&.btn-cancel::before {
font: normal normal normal 14px/1 ForkAwesome;
margin-right: 0.5em;
@@ -108,6 +110,7 @@ $chill-theme-buttons: (
&.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
}
@@ -133,3 +136,18 @@ $chill-theme-buttons: (
.btn-sm, .btn-group-sm > .btn {
min-width: 36px;
}
// Homepage special fast action buttons
div.sticky-buttons {
position: fixed;
bottom: 3em;
right: 2em;
.btn-circle {
width: 50px; height: 50px;
border-radius: 50%;
text-align: center;
padding: 0.45rem 0.7rem;
display: block;
margin-bottom: 0.5rem;
}
}

View File

@@ -39,6 +39,7 @@ var ShowHide = function(options) {
contents.push(el);
}
container_content.push(contents);
// console.log('container content', container_content);
}
// attach the listener on each input

View File

@@ -1,21 +1,19 @@
require('./blur.scss');
var toggleBlur = function(e){
var btn = e.target;
btn.previousElementSibling.classList.toggle("blur");
btn.classList.toggle("fa-eye");
btn.classList.toggle("fa-eye-slash");
}
var infos = document.getElementsByClassName("confidential");
for(var i=0; i < infos.length; i++){
infos[i].insertAdjacentHTML('beforeend', '<i class="fa fa-eye toggle" aria-hidden="true"></i>');
}
var toggles = document.getElementsByClassName("toggle");
for(var i=0; i < toggles.length; i++){
toggles[i].addEventListener("click", toggleBlur);
}
document.querySelectorAll('.confidential').forEach(function (el) {
let i = document.createElement('i');
const classes = ['fa', 'fa-eye', 'toggle'];
i.classList.add(...classes);
el.appendChild(i);
const toggleBlur = function(e) {
for (let child of el.children) {
if (!child.classList.contains('toggle')) {
child.classList.toggle('blur');
}
}
i.classList.toggle('fa-eye');
i.classList.toggle('fa-eye-slash');
}
i.addEventListener('click', toggleBlur);
toggleBlur();
});

View File

@@ -1,49 +1,34 @@
import { createApp } from "vue";
import PickWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
import ListWorkflowModalVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflowModal.vue';
import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
// pick workflow
document.querySelectorAll('[data-pick-workflow]')
.forEach(function(el) {
const app = {
components: {
PickWorkflowVue
},
template:
'<pick-workflow-vue ' +
':relatedEntityClass="relatedEntityClass" ' +
':relatedEntityId="relatedEntityId" ' +
':workflowsAvailables="workflowsAvailables" ' +
'></pick-workflow-vue>',
data() {
return {
relatedEntityClass: el.dataset.relatedEntityClass,
relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
}
}
};
createApp(app).mount(el);
})
;
const i18n = _createI18n({});
// list workflow
document.querySelectorAll('[data-list-workflows]')
.forEach(function (el) {
const app = {
components: {
ListWorkflowVue,
ListWorkflowModalVue,
},
template:
'<list-workflow-vue ' +
'<list-workflow-modal-vue ' +
':workflows="workflows" ' +
'></list-workflow-vue>',
':allowCreate="allowCreate" ' +
':relatedEntityClass="relatedEntityClass" ' +
':relatedEntityId="relatedEntityId" ' +
':workflowsAvailables="workflowsAvailables" ' +
'></list-workflow-modal-vue>',
data() {
return {
workflows: JSON.parse(el.dataset.workflows),
allowCreate: el.dataset.allowCreate === "1",
relatedEntityClass: el.dataset.relatedEntityClass,
relatedEntityId: Number.parseInt(el.dataset.relatedEntityId),
workflowsAvailables: JSON.parse(el.dataset.workflowsAvailables),
}
}
};
createApp(app).mount(el);
createApp(app).use(i18n).mount(el);
})
;

View File

@@ -5,18 +5,27 @@ import { appMessages } from 'ChillMainAssets/vuejs/PickEntity/i18n';
const i18n = _createI18n(appMessages);
window.addEventListener('DOMContentLoaded', function(e) {
let appsOnPage = new Map();
let apps = document.querySelectorAll('[data-module="pick-dynamic"]');
function loadDynamicPicker(element) {
let apps = element.querySelectorAll('[data-module="pick-dynamic"]');
apps.forEach(function(el) {
const
isMultiple = parseInt(el.dataset.multiple) === 1,
input = document.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
picked = isMultiple ? JSON.parse(input.value) : [JSON.parse(input.value)];
uniqId = el.dataset.uniqid,
input = element.querySelector('[data-input-uniqid="'+ el.dataset.uniqid +'"]'),
picked = (isMultiple) ? (JSON.parse(input.value)) : ((input.value === '[]') ? (null) : ([JSON.parse(input.value)]));
createApp({
if (!isMultiple) {
if (input.value === '[]'){
input.value = null;
}
}
const app = createApp({
template: '<pick-entity ' +
':multiple="multiple" ' +
':types="types" ' +
@@ -65,5 +74,36 @@ window.addEventListener('DOMContentLoaded', function(e) {
})
.use(i18n)
.mount(el);
appsOnPage.set(uniqId, app);
});
});
}
document.addEventListener('show-hide-show', function(e) {
console.log('creation event caught')
loadDynamicPicker(e.detail.container)
})
document.addEventListener('show-hide-hide', function(e) {
console.log('hiding event caught')
e.detail.container.querySelectorAll('[data-module="pick-dynamic"]').forEach((el) => {
let uniqId = el.dataset.uniqid;
console.log(uniqId);
if (appsOnPage.has(uniqId)) {
appsOnPage.get(uniqId).unmount();
console.log('App has been unmounted')
appsOnPage.delete(uniqId);
}
})
})
document.addEventListener('DOMContentLoaded', function(e) {
console.log('loaded event', e)
loadDynamicPicker(document)
})

View File

@@ -0,0 +1,16 @@
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: `<app></app>`,
})
.use(store)
.use(i18n)
.component('app', App)
.mount('#homepage_widget')
;

View File

@@ -562,6 +562,7 @@ export default {
this.entity.loaded.cities = [];
this.entity.loaded.countries = [];
this.entity.selected.confidential = this.context.edit ? this.entity.address.confidential : false;
this.entity.selected.isNoAddress = (this.context.edit && this.entity.address.text === '') ? true : false;
this.entity.selected.country = this.context.edit ? this.entity.address.country : {};
@@ -593,6 +594,7 @@ export default {
{
console.log('apply changes');
let newAddress = {
'confidential': this.entity.selected.confidential,
'isNoAddress': this.entity.selected.isNoAddress,
'street': this.entity.selected.isNoAddress ? '' : this.entity.selected.address.street,
'streetNumber': this.entity.selected.isNoAddress ? '' : this.entity.selected.address.streetNumber,

View File

@@ -6,7 +6,6 @@
<input class="form-control"
type="text"
name="floor"
maxlength=16
:placeholder="$t('floor')"
v-model="floor"/>
<label for="floor">{{ $t('floor') }}</label>
@@ -15,7 +14,6 @@
<input class="form-control"
type="text"
name="corridor"
maxlength=16
:placeholder="$t('corridor')"
v-model="corridor"/>
<label for="corridor">{{ $t('corridor') }}</label>
@@ -24,7 +22,6 @@
<input class="form-control"
type="text"
name="steps"
maxlength=16
:placeholder="$t('steps')"
v-model="steps"/>
<label for="steps">{{ $t('steps') }}</label>
@@ -33,7 +30,6 @@
<input class="form-control"
type="text"
name="flat"
maxlength=16
:placeholder="$t('flat')"
v-model="flat"/>
<label for="flat">{{ $t('flat') }}</label>

View File

@@ -17,12 +17,22 @@
<div class="row my-3">
<div class="col-lg-6">
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="isConfidential"
v-model="isConfidential"
:value="valueConfidential" />
<label class="form-check-label" for="isConfidential">
{{ $t('isConfidential') }}
</label>
</div>
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="isNoAddress"
v-model="isNoAddress"
v-bind:value="value" />
:value="value" />
<label class="form-check-label" for="isNoAddress">
{{ $t('isNoAddress') }}
</label>
@@ -118,7 +128,8 @@ export default {
emits: ['getCities', 'getReferenceAddresses'],
data() {
return {
value: false
value: false,
valueConfidential: false,
}
},
computed: {
@@ -134,6 +145,14 @@ export default {
addressMap() {
return this.entity.addressMap;
},
isConfidential: {
set(value) {
this.entity.selected.confidential = value;
},
get() {
return this.entity.selected.confidential;
}
},
isNoAddress: {
set(value) {
console.log('isNoAddress value', value);

View File

@@ -18,6 +18,7 @@ const addressMessages = {
other_address: 'Autre adresse',
create_address: 'Adresse inconnue. Cliquez ici pour créer une nouvelle adresse',
isNoAddress: 'Pas d\'adresse complète',
isConfidential: 'Adresse confidentielle',
street: 'Nom de rue',
streetNumber: 'Numéro',
floor: 'Étage',

View File

@@ -0,0 +1,140 @@
<template>
<h2>{{ $t('main_title') }}</h2>
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyCustoms'}"
@click="selectTab('MyCustoms')">
<i class="fa fa-dashboard"></i>
</a>
</li>
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyNotifications'}"
@click="selectTab('MyNotifications')">
{{ $t('my_notifications.tab') }}
<tab-counter :count="state.notifications.count"></tab-counter>
</a>
</li>
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyAccompanyingCourses'}"
@click="selectTab('MyAccompanyingCourses')">
{{ $t('my_accompanying_courses.tab') }}
<tab-counter :count="state.accompanyingCourses.count"></tab-counter>
</a>
</li>
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyWorks'}"
@click="selectTab('MyWorks')">
{{ $t('my_works.tab') }}
<tab-counter :count="state.works.count"></tab-counter>
</a>
</li>
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyEvaluations'}"
@click="selectTab('MyEvaluations')">
{{ $t('my_evaluations.tab') }}
<tab-counter :count="state.evaluations.count"></tab-counter>
</a>
</li>
<li class="nav-item">
<a class="nav-link"
:class="{'active': activeTab === 'MyTasks'}"
@click="selectTab('MyTasks')">
{{ $t('my_tasks.tab') }}
<tab-counter :count="state.tasks.warning.count"></tab-counter>
<tab-counter :count="state.tasks.alert.count"></tab-counter>
</a>
</li>
<li class="nav-item loading ms-auto py-2" v-if="loading">
<i class="fa fa-circle-o-notch fa-spin fa-lg text-chill-gray" :title="$t('loading')"></i>
</li>
</ul>
<div class="my-4">
<my-customs
v-if="activeTab === 'MyCustoms'">
</my-customs>
<my-works
v-else-if="activeTab === 'MyWorks'">
</my-works>
<my-evaluations
v-else-if="activeTab === 'MyEvaluations'">
</my-evaluations>
<my-tasks
v-else-if="activeTab === 'MyTasks'">
</my-tasks>
<my-accompanying-courses
v-else-if="activeTab === 'MyAccompanyingCourses'">
</my-accompanying-courses>
<my-notifications
v-else-if="activeTab === 'MyNotifications'">
</my-notifications>
</div>
</template>
<script>
import MyCustoms from './MyCustoms';
import MyWorks from './MyWorks';
import MyEvaluations from './MyEvaluations';
import MyTasks from './MyTasks';
import MyAccompanyingCourses from './MyAccompanyingCourses';
import MyNotifications from './MyNotifications';
import TabCounter from './TabCounter';
import { mapState } from "vuex";
export default {
name: "App",
components: {
MyCustoms,
MyWorks,
MyEvaluations,
MyTasks,
MyAccompanyingCourses,
MyNotifications,
TabCounter,
},
data() {
return {
activeTab: 'MyCustoms'
}
},
computed: {
...mapState([
'loading',
]),
// just to see all in devtool :
...mapState({
state: (state) => state,
}),
},
methods: {
selectTab(tab) {
this.$store.dispatch('getByTab', { tab: tab });
this.activeTab = tab;
}
},
mounted() {
for (const m of [
'MyNotifications',
'MyAccompanyingCourses',
'MyWorks',
'MyEvaluations',
'MyTasks',
]) {
this.$store.dispatch('getByTab', { tab: m, param: "countOnly=1" });
}
}
}
</script>
<style scoped>
a.nav-link {
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,60 @@
<template>
<div class="alert alert-light">{{ $t('my_accompanying_courses.description') }}</div>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">id</th>
<th scope="col">Ouvert le</th>
<th scope="col">Usagers concernés</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(c, i) in accompanyingCourses.results" :key="`course-${i}`">
<td>{{ c.id}}</td>
<td>{{ $d(c.openingDate.datetime, 'long') }}</td>
<td>{{ c.participations.length }}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(c)">
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
export default {
name: "MyAccompanyingCourses",
components: {
TabTable
},
computed: {
...mapState([
'accompanyingCourses',
]),
...mapGetters([
'isAccompanyingCoursesLoaded',
]),
noResults() {
if (!this.isAccompanyingCoursesLoaded) {
return false;
} else {
return this.accompanyingCourses.count === 0;
}
},
},
methods: {
getUrl(c) {
return `/fr/parcours/${c.id}`
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,77 @@
<template>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_dashboard') }}</span>
<div v-else id="dashboards" class="row g-3" data-masonry='{"percentPosition": true }'>
<div class="mbloc col col-sm-6 col-lg-4">
<div class="custom1">
<ul class="list-unstyled">
<li v-if="counter.notifications > 0">
<b>{{ counter.notifications }}</b> {{ $t('counter.unread_notifications') }}
</li>
<li v-if="counter.accompanyingCourses > 0">
<b>{{ counter.accompanyingCourses }}</b> {{ $t('counter.assignated_courses') }}
</li>
<li v-if="counter.works > 0">
<b>{{ counter.works }}</b> {{ $t('counter.assignated_actions') }}
</li>
<li v-if="counter.evaluations > 0">
<b>{{ counter.evaluations }}</b> {{ $t('counter.assignated_evaluations') }}
</li>
<li v-if="counter.tasksAlert > 0">
<b>{{ counter.tasksAlert }}</b> {{ $t('counter.alert_tasks') }}
</li>
<li v-if="counter.tasksWarning > 0">
<b>{{ counter.tasksWarning }}</b> {{ $t('counter.warning_tasks') }}
</li>
</ul>
</div>
</div>
<!--
<div class="mbloc col col-sm-6 col-lg-4">
<div class="custom2">
Mon dashboard personnalisé
</div>
</div>
<div class="mbloc col col-sm-6 col-lg-4">
<div class="custom3">
Mon dashboard personnalisé
</div>
</div>
<div class="mbloc col col-sm-6 col-lg-4">
<div class="custom4">
Mon dashboard personnalisé
</div>
</div>
-->
</div>
</template>
<script>
import { mapGetters } from "vuex";
import Masonry from 'masonry-layout/masonry';
export default {
name: "MyCustoms",
computed: {
...mapGetters(['counter']),
noResults() {
return false
},
},
mounted() {
const elem = document.querySelector('#dashboards');
const masonry = new Masonry(elem, {});
}
}
</script>
<style scoped>
div.custom4,
div.custom3,
div.custom2 {
font-style: italic;
color: var(--bs-chill-gray);
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<div class="alert alert-light">{{ $t('my_evaluations.description') }}</div>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">id</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(e, i) in evaluations.results" :key="`evaluation-${i}`">
<td>{{ e.id}}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(e)">
{{ $t('show_entity', { entity: $t('the_evaluation') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
export default {
name: "MyEvaluations",
components: {
TabTable
},
computed: {
...mapState([
'evaluations',
]),
...mapGetters([
'isEvaluationsLoaded',
]),
noResults() {
if (!this.isEvaluationsLoaded) {
return false;
} else {
return this.evaluations.count === 0;
}
}
},
methods: {
getUrl(e) {
let anchor = '#evaluations';
return `/fr/person/accompanying-period/work/${e.id}/edit${anchor}`
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,96 @@
<template>
<div class="alert alert-light">{{ $t('my_notifications.description') }}</div>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">{{ $t('Date') }}</th>
<th scope="col">{{ $t('Subject') }}</th>
<th scope="col">{{ $t('From') }}</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(n, i) in notifications.results" :key="`notify-${i}`">
<td>{{ $d(n.date.datetime, 'long') }}</td>
<td>
<span class="unread">
<i class="fa fa-envelope-o"></i>
<a :href="getNotificationUrl(n)">{{ n.title }}</a>
</span>
</td>
<td>{{ n.sender.text }}</td>
<td>
<a class="btn btn-sm btn-show"
:href="getEntityUrl(n)">
{{ $t('show_entity', { entity: getEntityName(n) }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
import { appMessages } from 'ChillMainAssets/vuejs/HomepageWidget/js/i18n';
export default {
name: "MyNotifications",
components: {
TabTable
},
computed: {
...mapState([
'notifications',
]),
...mapGetters([
'isNotificationsLoaded',
]),
noResults() {
if (!this.isNotificationsLoaded) {
return false;
} else {
return this.notifications.count === 0;
}
}
},
methods: {
getNotificationUrl(n) {
return `/fr/notification/${n.id}/show`
},
getEntityName(n) {
switch (n.relatedEntityClass) {
case 'Chill\\ActivityBundle\\Entity\\Activity':
return appMessages.fr.the_activity;
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
return appMessages.fr.the_course;
default:
throw 'notification type unknown';
}
},
getEntityUrl(n) {
switch (n.relatedEntityClass) {
case 'Chill\\ActivityBundle\\Entity\\Activity':
return `/fr/activity/${n.relatedEntityId}/show`
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
return `/fr/parcours/${n.relatedEntityId}`
default:
throw 'notification type unknown';
}
}
}
}
</script>
<style lang="scss" scoped>
span.unread {
font-weight: bold;
i {
margin-right: 0.5em;
}
a {
text-decoration: unset;
}
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<div class="alert alert-light">{{ $t('my_tasks.description_alert') }}</div>
<span v-if="noResultsWarning" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">id</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(t, i) in tasks.warning" :key="`task-warning-${i}`">
<td>{{ t.id}}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t('show_entity', { entity: $t('the_task') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
<div class="alert alert-light">{{ $t('my_tasks.description_warning') }}</div>
<span v-if="noResultsAlert" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">id</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(t, i) in tasks.alert" :key="`task-alert-${i}`">
<td>{{ t.id}}</td>
<td>
<a class="btn btn-sm btn-show" :href="getUrl(t)">
{{ $t('show_entity', { entity: $t('the_task') }) }}
</a>
</td>
</tr>
</template>
</tab-table>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
export default {
name: "MyTasks",
components: {
TabTable
},
computed: {
...mapState([
'tasks',
]),
...mapGetters([
'isTasksWarningLoaded',
'isTasksAlertLoaded',
]),
noResultsAlert() {
if (!this.isTasksAlertLoaded) {
return false;
} else {
return this.tasks.alert.count === 0;
}
},
noResultsWarning() {
if (!this.isTasksWarningLoaded) {
return false;
} else {
return this.tasks.warning.count === 0;
}
}
},
methods: {
getUrl(t) {
return `/fr/task/single-task/${t.id}/show`
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,79 @@
<template>
<div class="accompanying_course_work">
<div class="alert alert-light">{{ $t('my_works.description') }}</div>
<span v-if="noResults" class="chill-no-data-statement">{{ $t('no_data') }}</span>
<tab-table v-else>
<template v-slot:thead>
<th scope="col">{{ $t('StartDate') }}</th>
<th scope="col">{{ $t('SocialAction') }}</th>
<th scope="col"></th>
</template>
<template v-slot:tbody>
<tr v-for="(w, i) in works.results" :key="`works-${i}`">
<td>{{ $d(w.startDate.datetime, 'short') }}</td>
<td>
<h4 class="badge-title">
<span class="title_label"></span>
<span class="title_action">
{{ w.socialAction.text }}
</span>
</h4>
</td>
<td>
<div class="btn-group" role="group" aria-label="Actions">
<a class="btn btn-sm btn-update" :href="getUrl(w)">
{{ $t('show_entity', { entity: $t('the_action') }) }}
</a>
<a class="btn btn-sm btn-show" :href="getUrl(w.accompanyingPeriod)">
{{ $t('show_entity', { entity: $t('the_course') }) }}
</a>
</div>
</td>
</tr>
</template>
</tab-table>
</div>
</template>
<script>
import { mapState, mapGetters } from "vuex";
import TabTable from "./TabTable";
export default {
name: "MyWorks",
components: {
TabTable
},
computed: {
...mapState([
'works',
]),
...mapGetters([
'isWorksLoaded',
]),
noResults() {
if (!this.isWorksLoaded) {
return false;
} else {
return this.works.count === 0;
}
}
},
methods: {
getUrl(e) {
switch (e.type) {
case 'accompanying_period_work':
return `/fr/person/accompanying-period/work/${e.id}/edit`
case 'accompanying_period':
return `/fr/parcours/${e.id}`
default:
throw 'entity type unknown';
}
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,18 @@
<template>
<span v-if="isCounterAvailable"
class="badge rounded-pill bg-danger counter">
{{ count }}
</span>
</template>
<script>
export default {
name: "TabCounter",
props: ['count'],
computed: {
isCounterAvailable() {
return (typeof this.count !== 'undefined' && this.count > 0 )
}
}
}
</script>

View File

@@ -0,0 +1,21 @@
<template>
<table class="table table-striped table-hover">
<thead>
<tr>
<slot name="thead"></slot>
</tr>
</thead>
<tbody>
<slot name="tbody"></slot>
</tbody>
</table>
</template>
<script>
export default {
name: "TabTable",
props: []
}
</script>
<style scoped></style>

View File

@@ -0,0 +1,54 @@
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 parcours",
description: "Liste des parcours d'accompagnement que l'on vient de m'attribuer.",
},
my_notifications: {
tab: "Mes notifications",
description: "Liste des notifications reçues et non lues.",
},
Date: "Date",
From: "De",
Subject: "Objet",
Entity: "Associé à",
show_entity: "Voir {entity}",
the_activity: "l'échange",
the_course: "le parcours",
the_action: "l'action",
the_evaluation: "l'évaluation",
the_task: "la tâche",
StartDate: "Date d'ouverture",
SocialAction: "Action d'accompagnement",
no_data: "Aucun résultats",
no_dashboard: "Pas de tableaux de bord",
counter: {
unread_notifications: "notifications non lues",
assignated_courses: "parcours récents assignés",
assignated_actions: "actions assignées",
assignated_evaluations: "évaluations assignées",
alert_tasks: "tâches en rappel",
warning_tasks: "tâches à échéances",
}
}
};
Object.assign(appMessages.fr);
export {
appMessages
};

View File

@@ -0,0 +1,200 @@
import 'es6-promise/auto';
import { createStore } from 'vuex';
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import MyCustoms from "../MyCustoms";
import MyWorks from "../MyWorks";
import MyEvaluations from "../MyEvaluations";
import MyTasks from "../MyTasks";
import MyAccompanyingCourses from "../MyAccompanyingCourses";
import MyNotifications from "../MyNotifications";
const debug = process.env.NODE_ENV !== 'production';
const isEmpty = (obj) => {
return obj
&& Object.keys(obj).length <= 1
&& Object.getPrototypeOf(obj) === Object.prototype;
};
const store = createStore({
strict: debug,
state: {
works: {},
evaluations: {},
tasks: {
warning: {},
alert: {}
},
accompanyingCourses: {},
notifications: {},
errorMsg: [],
loading: false
},
getters: {
isWorksLoaded(state) {
return !isEmpty(state.works);
},
isEvaluationsLoaded(state) {
return !isEmpty(state.evaluations);
},
isTasksWarningLoaded(state) {
return !isEmpty(state.tasks.warning);
},
isTasksAlertLoaded(state) {
return !isEmpty(state.tasks.alert);
},
isAccompanyingCoursesLoaded(state) {
return !isEmpty(state.accompanyingCourses);
},
isNotificationsLoaded(state) {
return !isEmpty(state.notifications);
},
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,
}
}
},
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;
},
setLoading(state, bool) {
state.loading = bool;
},
catchError(state, error) {
state.errorMsg.push(error);
}
},
actions: {
getByTab({ commit, getters }, { tab, param }) {
switch (tab) {
case 'MyCustoms':
break;
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':
if (!getters.isEvaluationsLoaded) {
commit('setLoading', true);
const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
commit('addEvaluations', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case '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}`,
urlAlert = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=alert&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${'&'+ param}`
;
makeFetch('GET', urlWarning)
.then((response) => {
commit('addTasksWarning', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
makeFetch('GET', urlAlert)
.then((response) => {
commit('addTasksAlert', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case 'MyAccompanyingCourses':
if (!getters.isAccompanyingCoursesLoaded) {
commit('setLoading', true);
const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
commit('addCourses', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
case 'MyNotifications':
if (!getters.isNotificationsLoaded) {
commit('setLoading', true);
const url = `/api/1.0/main/notification/my/unread${'?'+ param}`;
makeFetch('GET', url)
.then((response) => {
commit('addNotifications', response);
commit('setLoading', false);
})
.catch((error) => {
commit('catchError', error);
throw error;
})
;
}
break;
default:
throw 'tab '+ tab;
}
}
},
});
export { store };

View File

@@ -1,5 +1,5 @@
<template>
<ul class="list-suggest remove-items">
<ul class="list-suggest remove-items" v-if="picked.length">
<li v-for="p in picked" @click="removeEntity(p)" :key="p.type+p.id">
<span class="chill_denomination">{{ p.text }}</span>
</li>
@@ -79,11 +79,15 @@ export default {
);
this.$refs.addPersons.resetSearch(); // to cast child method
modal.showModal = false;
console.log(this.picked)
},
removeEntity(entity) {
console.log('remove entity', entity);
this.$emit('removeEntity', entity);
}
},
mounted() {
console.log(this.picked);
}
}
</script>

View File

@@ -24,6 +24,11 @@
{{ $t('user')}}
</span>
<span v-if="entity.type === 'household'" class="badge rounded-pill bg-user">
{{ $t('household')}}
</span>
</template>
<script>
@@ -40,7 +45,8 @@ export default {
company: "Personne morale",
contact: "Personne physique",
},
user: 'TMS'
user: 'TMS',
household: 'Ménage',
}
}
}

View File

@@ -1,22 +1,26 @@
<template>
<div class="confidential" v-on:click="toggleBlur">
<div class="confidential-content blur">
<div :class="classes">
<div class="confidential-content" :class="{ 'blur': isBlurred }">
<slot name="confidential-content"></slot>
</div>
<i class="fa fa-eye toggle" aria-hidden="true"></i>
<div>
<i class="fa fa-eye toggle" aria-hidden="true" @click="toggleBlur"></i>
</div>
</div>
</template>
<script>
export default {
name: "Confidential",
data() {
return {
isBlurred: true,
};
},
methods : {
toggleBlur: function(e){
if(e.target.matches('.toggle')){
e.target.previousElementSibling.classList.toggle("blur");
e.target.classList.toggle("fa-eye");
e.target.classList.toggle("fa-eye-slash");
}
toggleBlur() {
console.log('toggle blur');
this.isBlurred = !this.isBlurred;
},
}
}
@@ -39,4 +43,4 @@ export default {
-moz-filter: blur(5px);
filter: blur(5px);
}
</style>
</style>

View File

@@ -1,26 +1,58 @@
<template>
<component :is="component" class="chill-entity entity-address my-3">
<component :is="component" class="address" :class="multiline">
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
<div v-if="isConfidential">
<confidential>
<template v-slot:confidential-content>
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
</template>
</confidential>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
<div v-if="!isConfidential">
<div v-if="isMultiline === true">
<p v-for="(l, i) in address.lines" :key="`line-${i}`">
{{ l }}
</p>
</div>
<div v-else>
<p v-if="address.text"
class="street">
{{ address.text }}
</p>
<p v-if="address.postcode"
class="postcode">
{{ address.postcode.code }} {{ address.postcode.name }}
</p>
<p v-if="address.country"
class="country">
{{ address.country.name.fr }}
</p>
</div>
</div>
</component>
<!-- <div v-if="isMultiline === true" class="address-more">
@@ -78,8 +110,14 @@
</template>
<script>
import Confidential from 'ChillMainAssets/vuejs/_components/Confidential.vue';
export default {
name: 'AddressRenderBox',
components: {
Confidential
},
props: {
address: {
type: Object
@@ -100,6 +138,9 @@ export default {
multiline() {
//console.log(this.isMultiline, typeof this.isMultiline);
return this.isMultiline === true ? "multiline" : "";
},
isConfidential() {
return this.address.confidential;
}
}
};

View File

@@ -1,26 +1,86 @@
<template>
<div class="list-group my-2 workflow workflow-box">
<div class="list-group-item">
<h4>Workflow associés</h4>
<div class="flex-table workflow" id="workflow-list">
<div v-for="(w, i) in workflows" :key="`workflow-${i}`"
class="item-bloc">
<div>
<div class="item-row col">
<h2>Workflow</h2>
<div class="flex-grow-1 ms-3 h3">
<div class="visually-hidden">
{{ w.relatedEntityClass }}
{{ w.relatedEntityId }}
</div>
</div>
</div>
<div class="breadcrumb">
<template v-for="(step, j) in w.steps" :key="`step-${j}`">
<span class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
data-bs-custom-class="workflow-transition"
:title="getPopTitle(step)"
:data-bs-content="getPopContent(step)">
<i v-if="step.currentStep.name === 'initial'"
class="fa fa-circle me-1 text-chill-yellow">
</i>
<i v-if="step.isFreezed"
class="fa fa-snowflake-o fa-sm me-1">
</i>
{{ step.currentStep.text }}
</span>
<span v-if="j !== Object.keys(w.steps).length - 1">
</span>
</template>
</div>
</div>
<div class="item-row">
<div class="item-col flex-grow-1">
<p v-if="isUserSubscribedToStep(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_all_steps') }}
</p>
<p v-if="isUserSubscribedToFinal(w)">
<i class="fa fa-check fa-fw"></i>
{{ $t('you_subscribed_to_final_step') }}
</p>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a :href="goToUrl(w)" class="btn btn-sm btn-show" :title="$t('action.show')"></a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="list-group-item" v-for="w in workflows">
{{ w.id }}
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-outline-primary"
title="voir"
:href="goToUrl(w)">
<i class="fa fa-eye fa-fw"></i>
</a>
</li>
</ul>
</div>
</div>
</template>
<script>
import Popover from 'bootstrap/js/src/popover';
const i18n = {
messages: {
fr: {
you_subscribed_to_all_steps: "Vous recevrez une notification à chaque étape",
you_subscribed_to_final_step: "Vous recevrez une notification à l'étape finale",
by: "Par",
at: "Le"
}
}
}
export default {
name: "ListWorkflow",
i18n: i18n,
props: {
workflows: {
type: Array,
@@ -30,7 +90,42 @@ export default {
methods: {
goToUrl(w) {
return `/fr/main/workflow/${w.id}/show`;
}
},
getPopTitle(step) {
if (step.transitionPrevious != null) {
let freezed = step.isFreezed ? `<i class="fa fa-snowflake-o fa-sm me-1"></i>` : ``;
return `${freezed}${step.currentStep.text}`;
}
},
getPopContent(step) {
if (step.transitionPrevious != null) {
return `<ul class="small_in_title">
<li><span class="item-key">${i18n.messages.fr.by} : </span><b>${step.transitionPreviousBy.text}</b></li>
<li><span class="item-key">${i18n.messages.fr.at} : </span><b>${this.formatDate(step.transitionPreviousAt.datetime)}</b></li>
</ul>`
;
}
},
formatDate(datetime) {
return datetime.split('T')[0] +' '+ datetime.split('T')[1].substring(0,5)
},
isUserSubscribedToStep(w) {
// todo
return false;
},
isUserSubscribedToFinal(w) {
// todo
return false;
},
},
mounted() {
const triggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
const popoverList = triggerList.map(function (el) {
//console.log('popover', el)
return new Popover(el, {
html: true,
});
});
}
}
</script>
</script>

View File

@@ -0,0 +1,111 @@
<template>
<button v-if="hasWorkflow"
class="btn btn-primary"
@click="openModal">
<b>{{ countWorkflows }}</b>
<template v-if="countWorkflows > 1">{{ $t('workflows') }}</template>
<template v-else>{{ $t('workflow') }}</template>
</button>
<pick-workflow v-else-if="allowCreate"
:relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables"
></pick-workflow>
<teleport to="body">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
<h2 class="modal-title">{{ $t('workflow_list') }}</h2>
</template>
<template v-slot:body>
<list-workflow-vue
:workflows="workflows"
></list-workflow-vue>
</template>
<template v-slot:footer>
<pick-workflow v-if="allowCreate"
:relatedEntityClass="this.relatedEntityClass"
:relatedEntityId="this.relatedEntityId"
:workflowsAvailables="workflowsAvailables"
></pick-workflow>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import PickWorkflow from 'ChillMainAssets/vuejs/_components/EntityWorkflow/PickWorkflow.vue';
import ListWorkflowVue from 'ChillMainAssets/vuejs/_components/EntityWorkflow/ListWorkflow.vue';
export default {
name: "ListWorkflowModal",
components: {
Modal,
PickWorkflow,
ListWorkflowVue
},
props: {
workflows: {
type: Array,
required: true,
},
allowCreate: {
type: Boolean,
required: true,
},
relatedEntityClass: {
type: String,
required: true,
},
relatedEntityId: {
type: Number,
required: false,
},
workflowsAvailables: {
type: Array,
required: true,
}
},
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl"
},
}
},
computed: {
countWorkflows() {
return this.workflows.length;
},
hasWorkflow() {
return this.countWorkflows > 0;
}
},
methods: {
openModal() {
this.modal.showModal = true;
},
},
i18n: {
messages: {
fr: {
workflow_list: "Liste des workflows associés",
workflow: " workflow associé",
workflows: " workflows associés",
}
}
}
}
</script>
<style scoped></style>

View File

@@ -61,14 +61,15 @@ const messages = {
woman: "Née le"
},
deathdate: "Date de décès",
years_old: "ans",
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"
holder: "Titulaire",
years_old: "an | {n} an | {n} ans",
}
}
};

View File

@@ -59,7 +59,7 @@
must be shown in such list
#}
{%- if render == 'list' -%}
<li class="chill-entity entity-address">
<li class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
{% if options['with_picto'] %}
<i class="fa fa-li fa-map-marker"></i>
{% endif %}
@@ -68,7 +68,7 @@
{%- endif -%}
{%- if render == 'inline' -%}
<span class="chill-entity entity-address">
<span class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
{% if options['with_picto'] %}
<i class="fa fa-fw fa-map-marker"></i>
{% endif %}
@@ -77,7 +77,7 @@
{%- endif -%}
{%- if render == 'bloc' -%}
<div class="chill-entity entity-address">
<div class="chill-entity entity-address {% if address.confidential %}confidential{% endif %}">
{% if options['has_no_address'] == true and address.isNoAddress == true %}
{% if address.postCode is not empty %}
<div class="address{% if options['multiline'] %} multiline{% endif %}{% if options['with_delimiter'] %} delimiter{% endif %}">

View File

@@ -216,7 +216,7 @@
{% endif %}
{% endblock %}
{% block pick_user_dynamic_widget %}
{% block pick_entity_dynamic_widget %}
<input type="hidden" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %} data-input-uniqid="{{ form.vars['uniqid'] }}"/>
<div data-module="pick-dynamic" data-types="{{ form.vars['types']|json_encode }}" data-multiple="{{ form.vars['multiple'] }}" data-uniqid="{{ form.vars['uniqid'] }}"></div>
{% endblock %}

View File

@@ -0,0 +1,3 @@
<div class="sticky-buttons">
{# Override this file to add fast actions buttons #}
</div>

View File

@@ -0,0 +1,15 @@
<div class="col-10 mt-5">
{# vue component #}
<div id="homepage_widget"></div>
{% include '@ChillMain/Homepage/fast_actions.html.twig' %}
</div>
{% block css %}
{{ encore_entry_link_tags('page_homepage_widget') }}
{% endblock %}
{% block js %}
{{ encore_entry_script_tags('page_homepage_widget') }}
{% endblock %}

View File

@@ -0,0 +1,77 @@
{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %}
{% macro recordAction(comment) %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_COMMENT_EDIT', comment) %}
<li>
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'edit': comment.id,
'id': comment.notification.id
}) }}" class="btn btn-edit" title="{{ 'Edit'|trans }}"
></a>
</li>
{% endif %}
{% endmacro %}
<div class="notification-comment-list my-5">
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
{% if notification.comments|length > 0 %}
<div class="flex-table">
{% for comment in notification.comments %}
{% if editedCommentForm is null or editedCommentId != comment.id %}
{{ m.show_comment(comment, {
'recordAction': _self.recordAction(comment)
}) }}
{% else %}
<div class="item-bloc">
<div class="item-row row">
<a id="comment-{{ comment.id }}"></a>
{{ form_start(editedCommentForm) }}
{{ form_errors(editedCommentForm) }}
{{ form_widget(editedCommentForm.content) }}
<input type="hidden" name="form" value="edit" />
<ul class="record_actions">
<li class="cancel">
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'id': notification.id }) }}" class="btn btn-cancel">
{{ 'cancel'|trans }}
</a>
</li>
<li>
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(editedCommentForm) }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% else %}
<span class="chill-no-data-statement">{{ 'No comments'|trans }}</span>
{% endif %}
{% if appendCommentForm is not null %}
<div class="new-comment my-5">
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
{{ form_start(appendCommentForm) }}
{{ form_errors(appendCommentForm) }}
{{ form_widget(appendCommentForm.content) }}
<input type="hidden" name="form" value="append" />
<ul class="record_actions">
<li>
<button class="btn btn-create" type="submit">{{ 'notification.append_comment'|trans }}</button>
</li>
</ul>
{{ form_end(appendCommentForm) }}
</div>
{% endif %}
</div>

View File

@@ -52,9 +52,11 @@
{% macro content(c) %}
<div class="item-row separator">
<div class="mx-3 flex-grow-1">
{% include c.data.template with c.data.template_data %}
</div>
{% if c.data is defined %}
<div class="mx-3 flex-grow-1">
{% include c.data.template with c.data.template_data %}
</div>
{% endif %}
</div>
<div class="item-row">
<div class="notification-content">
@@ -68,34 +70,44 @@
</div>
{% if c.action_button is not defined or c.action_button != false %}
<div class="item-row separator">
<ul class="record_actions">
<li>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ c.notification.id }}"
data-notification-current-is-read="{{ c.notification.isReadBy(app.user) }}"
data-container="notification-status"
></span>
</li>
{% if is_granted('CHILL_MAIN_NOTIFICATION_UPDATE', c.notification) %}
<div class="item-col item-meta">
{# TODO twig extension to count comments #}
<div class="comment-counter visually-hidden">
<span>x commentaires</span>
</div>
</div>
<div class="item-col">
<ul class="record_actions">
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_edit', {'id': c.notification.id}) }}"
class="btn btn-edit" title="{{ 'Edit'|trans }}"></a>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ c.notification.id }}"
data-notification-current-is-read="{{ c.notification.isReadBy(app.user) }}"
data-container="notification-status"
></span>
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_SEE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}"
class="btn {% if not c.notification.isSystem %}btn-show change-icon{% else %}btn-misc{% endif %}" title="{{ 'notification.see_comments_thread'|trans }}">
{% if not c.notification.isSystem() %}
<i class="fa fa-comment"></i>
{% else %}
{{ 'Read more'|trans }}
{% endif %}
</a>
</li>
{% endif %}
</ul>
{% if is_granted('CHILL_MAIN_NOTIFICATION_UPDATE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_edit', {'id': c.notification.id}) }}"
class="btn btn-edit" title="{{ 'Edit'|trans }}"></a>
</li>
{% endif %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_SEE', c.notification) %}
<li>
<a href="{{ chill_path_add_return_path('chill_main_notification_show', {'id': c.notification.id}) }}"
class="btn {% if not c.notification.isSystem %}btn-show change-icon{% else %}btn-misc{% endif %}" title="{{ 'notification.see_comments_thread'|trans }}">
{% if not c.notification.isSystem() %}
<i class="fa fa-comment"></i>
{% else %}
{{ 'Read more'|trans }}
{% endif %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
{% endmacro %}
@@ -107,15 +119,19 @@
<button type="button" class="accordion-button collapsed"
data-bs-toggle="collapse" data-bs-target="#flush-collapse-{{ notification.id }}"
aria-expanded="false" aria-controls="flush-collapse-{{ notification.id }}">
{{ _self.title(_context) }}
</button>
{{ _self.header(_context) }}
</div>
<div id="flush-collapse-{{ notification.id }}"
class="accordion-collapse collapse"
aria-labelledby="flush-heading-{{ notification.id }}"
data-bs-parent="#notification-fold">
{{ _self.content(_context) }}
</div>
{% else %}
{{ _self.title(_context) }}

View File

@@ -20,6 +20,8 @@
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
{% include handler.template(notification) with handler.templateData(notification) %}
<div class="mb-3 row">
<label class="col-form-label col-sm-4" for="notification_message">{{ form_label(form.message) }}</label>
@@ -28,8 +30,6 @@
</div>
</div>
{% include handler.template(notification) with handler.templateData(notification) %}
{{ form_end(form) }}
<ul class="record_actions sticky-form-buttons">

View File

@@ -1,46 +1,12 @@
<div class="list-group my-2 notification notification-box">
<div class="list-group-item">
<h4>{{ 'notification.Sent'|trans }}</h4>
</div>
{# TODO pagination or limit #}
<h1 class="mt-5"><a id="notification-list"></a>{{ 'notification.Notifications'|trans }}</h1>
<div class="flex-table accordion accordion-flush" id="notification-fold">
{% for notification in notifications %}
<div class="list-group-item notification-status {% if notification.isReadBy(app.user) %}read{% else %}unread{% endif %}">
{% if not notification.isSystem %}
{% if notification.sender == app.user %}
<h6 class="notification-title">
<abbr title="{{ 'Le ' ~ notification.date|format_date('long') ~ '\n' ~ notification.title }}">
{{ notification.date|format_datetime('short','short') }}
</abbr>
{# Vue component #}
<span class="notification_toggle_read_status"
data-notification-id="{{ notification.id }}"
data-notification-current-is-read="{{ notification.isReadBy(app.user) }}"
data-container="notification-status"
data-show-button-url="{{ chill_path_add_return_path('chill_main_notification_show', {'id': notification.id}, false) }}"
data-button-class="btn-outline-primary"
data-button-text="false"
></span>
</h6>
{% if notification.addressees|length > 0 %}
<abbr title="{{ 'notification.sent_to'|trans }}">{{ 'notification.to'|trans }}:</abbr>
{% endif %}
{% for a in notification.addressees %}
<span class="badge-user">
{{ a|chill_entity_render_string }}
</span>
{% endfor %}
{% else %}
<div>{{ 'notification.you were notified by %sender%'|trans({'%sender%': notification.sender|chill_entity_render_string }) }}</div>
{% endif %}
{% else %}
<div>{{ 'notification.you were notified by system'|trans }}</div>
{% endif %}
</div>
{% include 'ChillMainBundle:Notification:_list_item.html.twig' with {
'full_content': true,
'fold_item': true,
'action_button': true,
} %}{#
#}
{% endfor %}
</div>

View File

@@ -14,21 +14,6 @@
{{ encore_entry_link_tags('mod_notification_toggle_read_status') }}
{% endblock %}
{% import '@ChillPerson/AccompanyingCourse/Comment/macro_showItem.html.twig' as m %}
{% macro recordAction(comment) %}
{% if is_granted('CHILL_MAIN_NOTIFICATION_COMMENT_EDIT', comment) %}
<li>
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'edit': comment.id,
'id': comment.notification.id
}) }}" class="btn btn-edit" title="{{ 'Edit'|trans }}"
></a>
</li>
{% endif %}
{% endmacro %}
{% block content %}
<div class="col-10 notification notification-show">
@@ -41,70 +26,12 @@
'template_data': handler.getTemplateData(notification)
},
'action_button': false,
'full_content': true
'full_content': true,
'fold_item': false
} %}
</div>
<div class="notification-comment-list my-5">
<h2 class="chill-blue">{{ 'notification.comments_list'|trans }}</h2>
{% if notification.comments|length > 0 %}
<div class="flex-table">
{% for comment in notification.comments %}
{% if editedCommentForm is null or editedCommentId != comment.id %}
{{ m.show_comment(comment, {
'recordAction': _self.recordAction(comment)
}) }}
{% else %}
<div class="item-bloc">
<div class="item-row row">
<a id="comment-{{ comment.id }}"></a>
{{ form_start(editedCommentForm) }}
{{ form_errors(editedCommentForm) }}
{{ form_widget(editedCommentForm.content) }}
<input type="hidden" name="form" value="edit" />
<ul class="record_actions">
<li class="cancel">
<a href="{{ chill_path_forward_return_path('chill_main_notification_show', {
'_fragment': 'comment-' ~ comment.id,
'id': notification.id }) }}" class="btn btn-cancel">
{{ 'cancel'|trans }}
</a>
</li>
<li>
<button class="btn btn-save" type="submit">{{ 'Save'|trans }}</button>
</li>
</ul>
{{ form_end(editedCommentForm) }}
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if appendCommentForm is not null %}
<div class="new-comment my-5">
<h2 class="chill-blue mb-4">{{ 'Write a new comment'|trans }}</h2>
{{ form_start(appendCommentForm) }}
{{ form_errors(appendCommentForm) }}
{{ form_widget(appendCommentForm.content) }}
<input type="hidden" name="form" value="append" />
<ul class="record_actions">
<li>
<button class="btn btn-create" type="submit">{{ 'notification.append_comment'|trans }}</button>
</li>
</ul>
{{ form_end(appendCommentForm) }}
</div>
{% endif %}
</div>
{% include 'ChillMainBundle:Notification:_item_comments.html.twig' %}
<ul class="record_actions sticky-form-buttons">
<li class="cancel">

View File

@@ -42,6 +42,3 @@
{% endif %}
></span>
{{ encore_entry_script_tags('vue_onthefly') }}
{{ encore_entry_link_tags('vue_onthefly') }}

View File

@@ -5,14 +5,10 @@
{{ form_row(transition_form.transition) }}
<div id="finalizeAfter">
{{ form_row(transition_form.finalizeAfter) }}
</div>
{% if transition_form.freezeAfter is defined %}
{{ form_row(transition_form.freezeAfter) }}
{% endif %}
<div id="futureDestUsers">
{{ form_row(transition_form.future_dest_users) }}
</div>
@@ -31,7 +27,7 @@
{% else %}
<div class="alert alert-chill-yellow">
{% if entity_workflow.currentStep.isFinalizeAfter %}
{% if entity_workflow.currentStep.isFinal %}
<p>{{ 'workflow.This workflow is finalized'|trans }}</p>
{% else %}
<p>{{ 'workflow.You are not allowed to apply a transition on this workflow'|trans }}</p>

View File

@@ -1,13 +1,13 @@
{% set acl = "0" %}
{% if is_granted('CHILL_MAIN_WORKFLOW_CREATE', blank_workflow) %}
{# vue component #}
<div data-pick-workflow="1"
data-related-entity-class="{{ blank_workflow.relatedEntityClass }}"
data-related-entity-id="{{ blank_workflow.relatedEntityId }}"
data-workflows-availables="{{ workflows_availables|json_encode()|e('html_attr') }}"
></div>
{% set acl = "1" %}
{% endif %}
{% if entity_workflows|length > 0 %}
{# vue component #}
<div data-list-workflows="1" data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}"></div>
{% endif %}
{# vue component #}
<div data-list-workflows="1"
data-workflows="{{ entity_workflows_json|json_encode|e('html_attr') }}"
data-allow-create="{{ acl }}"
data-related-entity-class="{{ blank_workflow.relatedEntityClass }}"
data-related-entity-id="{{ blank_workflow.relatedEntityId }}"
data-workflows-availables="{{ workflows_availables|json_encode()|e('html_attr') }}"
></div>

View File

@@ -2,30 +2,42 @@
<div class="flex-table">
{% for step in entity_workflow.stepsChained %}
{% set place_labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %}
{% set place_label = place_labels is null ? step.currentStep : place_labels|localize_translatable_string %}
<div class="item-bloc {{ 'bloc' ~ step.id }} {% if loop.first %}initial{% endif %}">
<div class="item-row">
{% if loop.first and step.next is null %}
<div class="item-col">
{{ 'workflow.No transitions'|trans }}
</div>
{% else %}
<div class="item-col">
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
</div>
<div class="item-col flex-column align-items-end">
<div class="decided">
{{ place_label }}
</div>
{#
<div class="decided">
<i class="fa fa-times fa-fw text-danger"></i>
Refusé
</div>
#}
</div>
{% endif %}
<div class="item-col flex-column align-items-end">
<div class="decided">
{% if not loop.first %}
<i class="fa fa-check fa-fw text-success"></i>
{% endif %}
{{ step.currentStep }}
</div>
{#
<div class="decided">
<i class="fa fa-times fa-fw text-danger"></i>
Refusé
</div>
#}
</div>
</div>
{% if step.next is not null %}
{% set transition = chill_workflow_transition_by_string(step.entityWorkflow, step.transitionAfter) %}
{% set transition_labels = workflow_metadata(step.entityWorkflow, 'label', transition) %}
{% set transition_label = transition_labels is null ? step.transitionAfter : transition_labels|localize_translatable_string %}
{% set forward = workflow_metadata(step.entityWorkflow, 'isForward', transition) %}
<div class="item-row separator">
<div class="item-col" style="width: inherit;">
{% if step.transitionBy is not null %}
@@ -40,7 +52,12 @@
<div class="item-col flex-column align-items-end">
<div class="to-decision">
<i class="fa fa-share fa-fw text-secondary" title="transféré"></i>
{{ step.next.currentStep }}
{% if forward %}
<i class="fa fa-check fa-fw text-success"></i>
{% else %}
<i class="fa fa-times fa-fw text-danger"></i>
{% endif %}
{{ transition_label }}
</div>
</div>
</div>

View File

@@ -1,41 +1,62 @@
{% macro popoverContent(step) %}
<ul class="small_in_title">
<li>
<span class="item-key">{{ 'By'|trans ~ ' : ' }}</span>
<b>{{ step.transitionBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.transitionAt|format_datetime('short', 'short') }}</b>
</li>
{% if step.previous is not null %}
<li>
<span class="item-key">{{ 'By'|trans ~ ' : ' }}</span>
<b>{{ step.previous.transitionBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.previous.transitionAt|format_datetime('short', 'short') }}</b>
</li>
{% else %}
<li>
<span class="item-key">{{ 'workflow.Created by'|trans ~ ' : ' }}</span>
<b>{{ step.entityWorkflow.createdBy|chill_entity_render_box }}</b>
</li>
<li>
<span class="item-key">{{ 'Le'|trans ~ ' : ' }}</span>
<b>{{ step.entityWorkflow.createdAt|format_datetime('short', 'short') }}</b>
</li>
{% endif %}
</ul>
{% endmacro %}
{% macro popoverTitle(step) %}
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
{% if step.previous is not null %}
{% set transition = chill_workflow_transition_by_string(step.entityWorkflow, step.previous.transitionAfter) %}
{% set labels = workflow_metadata(step.entityWorkflow, 'label', transition) %}
{% set label = labels is null ? step.previous.transitionAfter : labels|localize_translatable_string %}
{{ label }}
{% endif %}
{% endmacro %}
{% macro breadcrumb(_ctx) %}
<div class="breadcrumb">
{% for step in _ctx.entity_workflow.stepsChained %}
{% if step.previous is null %}
{#
{% set popContent = "Point de départ du workflow" %}
{{ dump(step) }}
#}
{% set popContent = _self.popoverContent(step) %}
{% else %}
{% set popContent = _self.popoverContent(step.previous) %}
{% endif %}
{% set labels = workflow_metadata(_ctx.entity_workflow, 'label', step.currentStep) %}
{% set label = labels is null ? step.currentStep : labels|localize_translatable_string %}
{% set popTitle = _self.popoverTitle(step) %}
{% set popContent = _self.popoverContent(step) %}
<span class="mx-2"
tabindex="0"
data-bs-trigger="focus hover"
data-bs-toggle="popover"
data-bs-placement="bottom"
data-bs-custom-class="workflow-transition"
title="{{ step.currentStep }}"
title="{{ popTitle|e('html_attr') }}"
data-bs-content="{{ popContent|e('html_attr') }}"
>
{% if step.currentStep == 'initial' %}
<i class="fa fa-circle me-1 text-chill-yellow"></i>
{% endif %}
{{ step.currentStep }}
{% if step.previous is not null and step.previous.freezeAfter == true %}
<i class="fa fa-snowflake-o fa-sm me-1" title="{{ 'workflow.Freezed'|trans }}"></i>
{% endif %}
{{ label }}
</span>
{% if not loop.last %}

View File

@@ -13,6 +13,7 @@
{{ encore_entry_link_tags('mod_ckeditor5') }}
{{ encore_entry_link_tags('chill') }}
{{ encore_entry_link_tags('mod_blur') }}
{{ encore_entry_link_tags('vue_onthefly') }}
{% block css %}<!-- nothing added to css -->{% endblock %}
</head>
@@ -59,26 +60,26 @@
{% endif %}
{% block content %}
<div class="col-8 main_search">
<h2>{{ 'Search'|trans }}</h2>
<form action="{{ path('chill_main_search') }}" method="get">
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
<center>
<button type="submit" class="btn btn-lg btn-warning mt-3">
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
</button>
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
</a>
</center>
</form>
</div>
<div class="col-8">
<a href="{{ path('chill_crud_aside_activity_new', {'type' : 7, 'duration' : '600', 'note' : 'Pas des remarques' }) }}"><div class="bloc btn btn-success btn-md btn-block">Appel téléphonique</div></a>
</div>
{{ chill_widget('homepage', {} ) }}
<div class="col-8 main_search">
<h2>{{ 'Search'|trans }}</h2>
<form action="{{ path('chill_main_search') }}" method="get">
<input class="form-control form-control-lg" name="q" type="search" placeholder="{{ 'Search persons, ...'|trans }}" />
<center>
<button type="submit" class="btn btn-lg btn-warning mt-3">
<i class="fa fa-fw fa-search"></i> {{ 'Search'|trans }}
</button>
<a class="btn btn-lg btn-misc mt-3" href="{{ path('chill_main_advanced_search_list') }}">
<i class="fa fa-fw fa-search"></i> {{ 'Advanced search'|trans }}
</a>
</center>
</form>
</div>
{# DISABLED {{ chill_widget('homepage', {} ) }} #}
{% include '@ChillMain/Homepage/index.html.twig' %}
{% endblock %}
</div>
@@ -94,6 +95,7 @@
{{ encore_entry_script_tags('mod_ckeditor5') }}
{{ encore_entry_script_tags('mod_blur') }}
{{ encore_entry_script_tags('chill') }}
{{ encore_entry_script_tags('vue_onthefly') }}
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', function(e) {

View File

@@ -45,13 +45,15 @@ class SearchUserApiProvider implements SearchApiInterface
$query
->setSelectKey('user')
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical),
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))', [$pattern, $pattern])
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.label),
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical))', [$pattern, $pattern])
->setFromClause('users AS u')
->setWhereClauses('SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
OR
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15
', [$pattern, $pattern]);
->setWhereClauses('
SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
OR u.usernamecanonical LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
OR SIMILARITY(LOWER(UNACCENT(?)), LOWER(UNACCENT(u.label))) > 0.15
OR u.label LIKE \'%\' || LOWER(UNACCENT(?)) || \'%\'
', [$pattern, $pattern, $pattern, $pattern]);
return $query;
}

View File

@@ -22,6 +22,10 @@ class SearchApiQuery
private array $fromClauseParams = [];
private bool $isDistinct = false;
private ?string $isDistinctKey = null;
private ?string $jsonbMetadata = null;
private array $jsonbMetadataParams = [];
@@ -105,6 +109,11 @@ class SearchApiQuery
]);
}
public function getDistinct(): bool
{
return $this->isDistinct;
}
public function getFromClause(): string
{
return $this->fromClause;
@@ -139,6 +148,14 @@ class SearchApiQuery
return $this;
}
public function setDistinct(bool $distinct, string $distinctKey): self
{
$this->isDistinct = $distinct;
$this->isDistinctKey = $distinctKey;
return $this;
}
public function setFromClause(string $fromClause, array $params = []): self
{
$this->fromClause = $fromClause;
@@ -185,7 +202,11 @@ class SearchApiQuery
private function buildSelectClause(bool $countOnly = false): string
{
if ($countOnly) {
return 'count(*) AS c';
if (!$this->isDistinct) {
return 'count(*) AS c';
}
return 'count(distinct ' . $this->isDistinctKey . ') AS c';
}
$selects = $this->getSelectClauses();
@@ -202,7 +223,7 @@ class SearchApiQuery
$selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]);
}
return implode(', ', $selects);
return ($this->isDistinct ? 'DISTINCT ' : '') . implode(', ', $selects);
}
private function buildSelectParams(bool $count = false): array

Some files were not shown because too many files have changed in this diff Show More