Compare commits
1605 Commits
release/4.
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f182be3db0 | ||
|
|
81a32f6d58 | ||
|
|
82552b1748 | ||
|
|
af7a45d739 | ||
|
|
21013a6fe5 | ||
|
|
c81ad67844 | ||
|
|
13797f1fb3 | ||
|
|
ef5cd39b16 | ||
|
|
e133e944e3 | ||
|
|
2ffb5bbb63 | ||
|
|
0f16644944 | ||
|
|
0b41159ee4 | ||
|
|
2a78aea898 | ||
|
|
4c5face779 | ||
|
|
b3a8a8d30c | ||
|
|
7836750171 | ||
|
|
ef9d3d2e8f | ||
|
|
3ca846b2c2 | ||
|
|
ae7b68bc16 | ||
|
|
3150134e65 | ||
|
|
b552628b43 | ||
|
|
9b3eb4702b | ||
|
|
52fa98f918 | ||
|
|
1d874f37b2 | ||
|
|
582ba6023e | ||
|
|
fbcb522b43 | ||
|
|
1d3096d82c | ||
|
|
43ea2e7857 | ||
|
|
92038a1949 | ||
|
|
a323ddd5dd | ||
|
|
e70cdd4cc5 | ||
|
|
427a81cff6 | ||
|
|
4933c7a678 | ||
|
|
8ee2ec5749 | ||
|
|
ce4216e56c | ||
|
|
a3526d338f | ||
|
|
65041f77c3 | ||
|
|
4206993c72 | ||
|
|
a7d8dc6a66 | ||
|
|
56bdf1bc4d | ||
|
|
acb009a909 | ||
|
|
96532fa61e | ||
|
|
eed7f1917e | ||
|
|
51b99089e4 | ||
|
|
118e35e897 | ||
|
|
b9197b1eb3 | ||
|
|
568bb696a7 | ||
|
|
43616297c8 | ||
|
|
51801a03a0 | ||
|
|
59254c0294 | ||
|
|
61b238d511 | ||
|
|
d5ca5331a7 | ||
|
|
8f72d8608b | ||
|
|
425f3da9d4 | ||
|
|
34dc025b79 | ||
|
|
b2f8cda99b | ||
|
|
dd1e0c672b | ||
|
|
0e3cf56034 | ||
|
|
bfe409305c | ||
|
|
b225917a51 | ||
|
|
5359f5aca9 | ||
|
|
1d2f450ba4 | ||
|
|
4aa03ba3c5 | ||
|
|
118e34b380 | ||
|
|
ff3c5768c6 | ||
|
|
f293eefe8c | ||
|
|
49c59897bf | ||
|
|
0e53484b62 | ||
|
|
842bd4e251 | ||
|
|
003817ed9d | ||
|
|
fb68e63bd0 | ||
|
|
e6a03c3b30 | ||
|
|
fb8b3a3b09 | ||
|
|
43fd966bf1 | ||
|
|
315fa5a440 | ||
|
|
3be593f464 | ||
|
|
8c47a8b758 | ||
|
|
5515a5b4d6 | ||
|
|
cee0a92430 | ||
|
|
ddc163f286 | ||
|
|
81ce0bc708 | ||
|
|
29c870631b | ||
|
|
261674339a | ||
|
|
bfb418fdfd | ||
|
|
5c8c191151 | ||
|
|
9e558944d1 | ||
|
|
d8134f6d42 | ||
|
|
aa3ae524f4 | ||
|
|
cd2b12c35a | ||
|
|
b7e1e5764e | ||
|
|
9ef4ba4af5 | ||
|
|
86deaceade | ||
|
|
fd76d110d4 | ||
|
|
21f67651ff | ||
|
|
0cf22e96bc | ||
|
|
86eeab5e69 | ||
|
|
e8c98be687 | ||
|
|
bf04423524 | ||
|
|
25df51af78 | ||
|
|
6ef4b981fc | ||
|
|
4bd13949f6 | ||
|
|
5fea526d6c | ||
|
|
0b367efa27 | ||
|
|
8d60e8d864 | ||
|
|
047a599809 | ||
|
|
44d361b658 | ||
|
|
780976243a | ||
|
|
820dab3bfb | ||
|
|
67020213ed | ||
|
|
13e019b5e5 | ||
|
|
78c8dcb8e6 | ||
|
|
db90ae538e | ||
|
|
97484c5f1a | ||
|
|
1f6bdc30c8 | ||
|
|
19dd650510 | ||
|
|
72d57ca342 | ||
|
|
fc3258ad9a | ||
|
|
2632675cbe | ||
|
|
b7c820e87c | ||
|
|
3578ed8512 | ||
|
|
7c719b4d26 | ||
|
|
4e3de53e45 | ||
|
|
13cba9831c | ||
|
|
c02863b079 | ||
|
|
27e64380b0 | ||
|
|
2699b36ab0 | ||
|
|
420c59a862 | ||
|
|
5ece842b2c | ||
|
|
6f77cef2ab | ||
|
|
54b59ab90c | ||
|
|
01fb7f0a1a | ||
|
|
d7586c2705 | ||
|
|
b00ff1186d | ||
|
|
9bea3c487a | ||
|
|
2b2c72fe04 | ||
|
|
edadf1ea41 | ||
|
|
c8e5752395 | ||
|
|
bac2d96605 | ||
|
|
063d40f845 | ||
|
|
881ad3a110 | ||
|
|
c248f55fbd | ||
|
|
2501bf5d56 | ||
|
|
48c8613f9b | ||
|
|
b26053efa4 | ||
|
|
32ef5393e2 | ||
|
|
abc54fae4f | ||
|
|
d094001792 | ||
|
|
573e1f3865 | ||
|
|
d99f96b079 | ||
|
|
4b68708624 | ||
|
|
7e764cdf62 | ||
|
|
32dd6e6ced | ||
|
|
fb9242f637 | ||
|
|
acf4a4f4d7 | ||
|
|
260a53aaf1 | ||
|
|
42d4829f2e | ||
|
|
6ac6f0a441 | ||
|
|
54ad666355 | ||
|
|
43f4cc6c9e | ||
|
|
6219ceb0a2 | ||
|
|
725c99211b | ||
|
|
bcf29eea45 | ||
|
|
0d4546bb9e | ||
|
|
e4ce12057b | ||
|
|
19ecd12c5f | ||
|
|
b96e55f42c | ||
|
|
31d4018698 | ||
|
|
ef26003755 | ||
|
|
931d311281 | ||
|
|
01eda98415 | ||
|
|
1b0fe9d352 | ||
|
|
f31db95241 | ||
|
|
ebedde5ed6 | ||
|
|
8e2ab6c2c4 | ||
|
|
e1308b6fd5 | ||
|
|
a5b3caea11 | ||
|
|
de1495ee2e | ||
|
|
7a78dd6547 | ||
|
|
8cdb869a10 | ||
|
|
f5639ec79d | ||
|
|
3d31728194 | ||
|
|
a996eaf53c | ||
|
|
f0b6cf9712 | ||
|
|
470685a94f | ||
|
|
c10b2c49b5 | ||
|
|
f1aa0e4817 | ||
|
|
82e31fffd8 | ||
|
|
cdc5d2ee6c | ||
|
|
3d26a73b05 | ||
|
|
b1fb38027d | ||
|
|
eef1139c0d | ||
|
|
cb0bdb89c5 | ||
|
|
d6a4b11b65 | ||
|
|
a80a75ca77 | ||
|
|
a56c66d19f | ||
|
|
bc7e2483c2 | ||
|
|
00decf1f6c | ||
|
|
306b524dee | ||
|
|
f26772d4a0 | ||
|
|
e39ab31397 | ||
|
|
208a1568b3 | ||
|
|
a7a4c18715 | ||
|
|
9228976803 | ||
|
|
6619b9f4de | ||
|
|
9f1f654521 | ||
|
|
ad974ef07b | ||
|
|
ff8009167b | ||
|
|
b3dc8b04c8 | ||
|
|
5684f97389 | ||
|
|
bce93733c9 | ||
|
|
c677bc17e4 | ||
|
|
91e344ae28 | ||
|
|
f4ad886a79 | ||
|
|
ef51c2dbe3 | ||
|
|
8984078767 | ||
|
|
59231003ea | ||
|
|
fb2fa6e6c2 | ||
|
|
dc253b0254 | ||
|
|
f3c1de430b | ||
|
|
af5adade4d | ||
|
|
d150fe6f80 | ||
|
|
ff132b2d87 | ||
|
|
90e8af9765 | ||
|
|
ab27b6f63b | ||
|
|
dcc711d4eb | ||
|
|
95d57642c4 | ||
|
|
7252119228 | ||
|
|
0adf3288c8 | ||
|
|
b30a6e159c | ||
|
|
3c8a814abb | ||
|
|
42093b81b2 | ||
|
|
f11ffa18a1 | ||
|
|
8f541081ae | ||
|
|
6a215c0440 | ||
|
|
ada527370f | ||
|
|
97f0230ac5 | ||
|
|
1070d62e47 | ||
|
|
ba3cb01f02 | ||
|
|
c52116f4e7 | ||
|
|
c787881c76 | ||
|
|
fa7fd420a8 | ||
|
|
12d30af308 | ||
|
|
4695d31c72 | ||
|
|
866e6ac45a | ||
|
|
b1f8453daf | ||
|
|
317d6d576b | ||
|
|
d66ea4e154 | ||
|
|
bcfb915d93 | ||
|
|
e5c3d9c07b | ||
|
|
8a8c1b957f | ||
|
|
76b7ec9bc5 | ||
|
|
78fa17dc73 | ||
|
|
e678f6f4b3 | ||
|
|
2eb6d7b27c | ||
|
|
5805bb2a33 | ||
|
|
e92c7632d5 | ||
|
|
370b116656 | ||
|
|
35ebe4774f | ||
|
|
8fab3fc04d | ||
|
|
3dc63bf77c | ||
|
|
305bde5561 | ||
|
|
048356bf3f | ||
|
|
b389b07a6e | ||
|
|
7f72ef0021 | ||
|
|
395c9b0cd3 | ||
|
|
7e44bdf1e2 | ||
|
|
9e0ec97845 | ||
|
|
0259bcee06 | ||
|
|
64e7cce171 | ||
|
|
4e39914cff | ||
|
|
5347f986e4 | ||
|
|
7dd24698b1 | ||
|
|
a8534a8b13 | ||
|
|
d15cedd6d1 | ||
|
|
51e46f20f4 | ||
|
|
3a5a91f1e1 | ||
|
|
7e910b9339 | ||
|
|
0fbf580293 | ||
|
|
d8b20774f4 | ||
|
|
63af59e624 | ||
|
|
61e9df1844 | ||
|
|
a2823df27e | ||
|
|
7557025b01 | ||
|
|
96662844b2 | ||
|
|
16e7cdaa1f | ||
|
|
feffcd5cdc | ||
|
|
89d26778f2 | ||
|
|
b7ea2ac415 | ||
|
|
bb1f86c9d2 | ||
|
|
cc972db41d | ||
|
|
d9ce51f7e3 | ||
|
|
5dd75f836c | ||
|
|
b45627b4a7 | ||
|
|
404e783060 | ||
|
|
3515b9a942 | ||
|
|
b244d1e4b4 | ||
|
|
c4729167cd | ||
|
|
a7b652c137 | ||
|
|
d396b782a7 | ||
|
|
965166a349 | ||
|
|
733f811f9d | ||
|
|
7e42932028 | ||
|
|
cdc58066f6 | ||
|
|
224dafcae6 | ||
|
|
bb3f59f1bb | ||
|
|
876127d1fb | ||
|
|
f27807097d | ||
|
|
68bfcf6700 | ||
|
|
829e009d4c | ||
|
|
6dbe365362 | ||
|
|
db1d65e601 | ||
|
|
767e688d3c | ||
|
|
e647d4b355 | ||
|
|
b85559c686 | ||
|
|
6f353ce01d | ||
|
|
5dfea0175d | ||
|
|
b4c0ce17de | ||
|
|
c805887bbd | ||
|
|
d4b419ded8 | ||
|
|
a5e70e9def | ||
|
|
d06cd4be54 | ||
|
|
20d0334489 | ||
|
|
5438e800c7 | ||
|
|
4593b09bf3 | ||
|
|
33ed133f9f | ||
|
|
fe2214d12c | ||
|
|
2e41ac041e | ||
|
|
98c3f749d8 | ||
|
|
851b9fb3b2 | ||
|
|
6aea290693 | ||
|
|
ace0ecfce1 | ||
|
|
a8c4f6dd29 | ||
|
|
3818741453 | ||
|
|
da3c18fde2 | ||
|
|
9836c2aab5 | ||
|
|
46202a0b64 | ||
|
|
16a8d91815 | ||
|
|
6f5cd79a7f | ||
|
|
bf7fab3600 | ||
|
|
d4653ca983 | ||
|
|
c9444d7747 | ||
|
|
287b95a538 | ||
|
|
686f390b1a | ||
|
|
46fba3ca5a | ||
|
|
7d2f07d6b0 | ||
|
|
6f9d2a7c85 | ||
|
|
59998396d4 | ||
|
|
60e151db0f | ||
|
|
211d81ab7a | ||
|
|
eb255daac2 | ||
|
|
032b1eeabe | ||
|
|
4526138fae | ||
|
|
428f1dd5fb | ||
|
|
4f4f286a5e | ||
|
|
05799650e6 | ||
|
|
d67bef3001 | ||
|
|
76d329b888 | ||
|
|
0329e74bbd | ||
|
|
9fabb7223a | ||
|
|
671c44438d | ||
|
|
2377fbff55 | ||
|
|
af17c026ff | ||
|
|
7a1e968a52 | ||
|
|
8e9f6e19bf | ||
|
|
bef117a22c | ||
|
|
d4a50efc38 | ||
|
|
6693933941 | ||
|
|
b627b181e3 | ||
|
|
fcf159d091 | ||
|
|
aa9de3c369 | ||
|
|
b989da3f05 | ||
|
|
cdf39fd838 | ||
|
|
c3fbb9100a | ||
|
|
fbc7dfce60 | ||
|
|
bde8bd8475 | ||
|
|
c713b9d2f4 | ||
|
|
25a0848c3e | ||
|
|
ccd008666e | ||
|
|
40d9eb15cf | ||
|
|
fad870e83e | ||
|
|
0f494b10d0 | ||
|
|
b2091d4f83 | ||
|
|
7b9c0d85cd | ||
|
|
5662cccc98 | ||
|
|
2b6001402a | ||
|
|
8d9df1f397 | ||
|
|
aa30982ff1 | ||
|
|
c5f072f6b6 | ||
|
|
fb8e66e32d | ||
|
|
d457f9a46f | ||
|
|
6bbe992940 | ||
|
|
1cb658f9d1 | ||
|
|
b3de35eac7 | ||
|
|
5bb7c780a1 | ||
|
|
5c5d1dc877 | ||
|
|
8ab324f69e | ||
|
|
e30a54297a | ||
|
|
c4fc832ef6 | ||
|
|
750312c59e | ||
|
|
e8665f7484 | ||
|
|
81669177cd | ||
|
|
dc063218ff | ||
|
|
004025636c | ||
|
|
5a2e806e4a | ||
|
|
b918933665 | ||
|
|
e39e2522aa | ||
|
|
8caa92d663 | ||
|
|
0be67a8589 | ||
|
|
d0250f3fd0 | ||
|
|
a11bb2282f | ||
|
|
664410821c | ||
|
|
a2a0defa23 | ||
|
|
c6595c6481 | ||
|
|
cbedb8370b | ||
|
|
b92d9c9e04 | ||
|
|
bd054ecedd | ||
|
|
9dfa101afe | ||
|
|
f52a378280 | ||
|
|
bda96f952e | ||
|
|
79fb9bd785 | ||
|
|
5de6b359ee | ||
|
|
b5f1afb81e | ||
|
|
63e53cb2ed | ||
|
|
7f08355311 | ||
|
|
76403ef8ea | ||
|
|
12768b5219 | ||
|
|
aa2012bbda | ||
|
|
179abe4f37 | ||
|
|
43154346e3 | ||
|
|
1b9128a68f | ||
|
|
b5b465d56f | ||
|
|
94cbb2cf3d | ||
|
|
a51e68c3d8 | ||
|
|
7208126584 | ||
|
|
256305d12f | ||
|
|
7996bc7e23 | ||
|
|
e86a7cf59f | ||
|
|
6abcbc96ac | ||
|
|
b23c3e60d7 | ||
|
|
2f6d460442 | ||
|
|
3cd1eaeddc | ||
|
|
feb3aa34e9 | ||
|
|
7f8c622f6b | ||
|
|
6628cf3495 | ||
|
|
e6f3cd77de | ||
|
|
8705524a4b | ||
|
|
4475f5979e | ||
|
|
93321437c1 | ||
|
|
0bf7f5f9ce | ||
|
|
ef8b1bc58e | ||
|
|
8f938c825d | ||
|
|
766ba97a0e | ||
|
|
24c433dd5b | ||
|
|
570dc20e8e | ||
|
|
34be5155c4 | ||
|
|
e752e4607c | ||
|
|
9e5ab63434 | ||
|
|
66cd961fc3 | ||
|
|
6216dd8bac | ||
|
|
3677b7d761 | ||
|
|
eae53022ee | ||
|
|
24e793582d | ||
|
|
01b8948b6a | ||
|
|
1b3dbfc98d | ||
|
|
a2930ccfdd | ||
|
|
201f4ca07f | ||
|
|
e81e8e7252 | ||
|
|
9d01fb5784 | ||
|
|
9921e868bc | ||
|
|
9dd6085bcf | ||
|
|
2d1928ebd0 | ||
|
|
095853d090 | ||
|
|
fe46e69c53 | ||
|
|
a55571a381 | ||
|
|
7d449d3ff7 | ||
|
|
db1257751a | ||
|
|
43830f463d | ||
|
|
e798197110 | ||
|
|
35f2161913 | ||
|
|
6f8edaf4ba | ||
|
|
80a713d850 | ||
|
|
ed518d6910 | ||
|
|
7ad36ea6f6 | ||
|
|
65fc2971ac | ||
|
|
4438f90af4 | ||
|
|
2daa033b8e | ||
|
|
726f909589 | ||
|
|
6a6db17d2c | ||
|
|
021775d3a7 | ||
|
|
88ef8830c2 | ||
|
|
eb9bd777e0 | ||
|
|
e3ef820bac | ||
|
|
9317f55d81 | ||
|
|
5d7849be45 | ||
|
|
74b4c1a11f | ||
|
|
0f79f04f52 | ||
|
|
ea07686946 | ||
|
|
3df6005549 | ||
|
|
511f24e3ef | ||
|
|
4e49d2d7a6 | ||
|
|
c7e03043e0 | ||
|
|
f85626a8e4 | ||
|
|
6282675682 | ||
|
|
747d8157b5 | ||
|
|
1bcd1869f0 | ||
|
|
95c273e7b2 | ||
|
|
6236275528 | ||
|
|
d654bcfc4b | ||
|
|
fcbffdad8f | ||
|
|
8079079217 | ||
|
|
87fa2df399 | ||
|
|
5d6d69c8c4 | ||
|
|
538f36aebc | ||
|
|
9067272382 | ||
|
|
bc430a796f | ||
|
|
24fc875cc9 | ||
|
|
fc8a9da8cd | ||
|
|
486ef92b07 | ||
|
|
630db9071a | ||
|
|
8e2219235d | ||
|
|
0a05d02623 | ||
|
|
f242c52a0c | ||
|
|
6e115038b0 | ||
|
|
428debcd6f | ||
|
|
ddbef9bd11 | ||
|
|
1dc013e46b | ||
|
|
d409811aa6 | ||
|
|
ecb7b89f31 | ||
|
|
8a3d372223 | ||
|
|
3544538c19 | ||
|
|
f82cdcf5bb | ||
|
|
e6a3b1a585 | ||
|
|
8d970779f5 | ||
|
|
954fba26f7 | ||
|
|
25418ced4a | ||
|
|
591b6eee36 | ||
|
|
d7cc8e3232 | ||
|
|
0d7adb4e6b | ||
|
|
62b8a55406 | ||
|
|
c4c549fe1b | ||
|
|
e41ac70dfe | ||
|
|
59ce4177d2 | ||
|
|
926d14056b | ||
|
|
937803fd75 | ||
|
|
168730cfca | ||
|
|
87c4becbe6 | ||
|
|
f0535eb47f | ||
|
|
522a78d6a2 | ||
|
|
4f955bcd50 | ||
|
|
d9ccf89166 | ||
|
|
0af6e45868 | ||
|
|
b1c40806c5 | ||
|
|
826c564af6 | ||
|
|
b623449efb | ||
|
|
221948411a | ||
|
|
fcba520003 | ||
|
|
f9c8571c31 | ||
|
|
90ba67a79c | ||
|
|
e78dcff3a7 | ||
|
|
40897cf2b3 | ||
|
|
1bbef9eb37 | ||
|
|
9b770b2be7 | ||
|
|
77b133757e | ||
|
|
37b692c260 | ||
|
|
dc2ad98120 | ||
|
|
91d1ee28f6 | ||
|
|
e2b7807cf3 | ||
|
|
46375a3db4 | ||
|
|
0585800532 | ||
|
|
eccb00345a | ||
|
|
621b8185fd | ||
|
|
907249cc40 | ||
|
|
35342707f0 | ||
|
|
0a7919f786 | ||
|
|
c1fc52223b | ||
|
|
246e69d754 | ||
|
|
16927903cd | ||
|
|
862b420488 | ||
|
|
d445c5f31c | ||
|
|
c4625d2e7e | ||
|
|
3f14690c39 | ||
|
|
30c1f8628b | ||
|
|
f18cd31a72 | ||
|
|
9b197811d1 | ||
|
|
e2846a3178 | ||
|
|
ed8eb4840e | ||
|
|
f1b7effe3a | ||
|
|
8390687c5a | ||
|
|
7f7148c859 | ||
|
|
66df5f807c | ||
|
|
53044bd391 | ||
|
|
2b4c52c13b | ||
|
|
6540684063 | ||
|
|
171e18c8a9 | ||
|
|
5ddae5f664 | ||
|
|
f25d134590 | ||
|
|
dbc609765b | ||
|
|
aaef6c7649 | ||
|
|
333f5c7fc5 | ||
|
|
47b16d2287 | ||
|
|
c1a8898e92 | ||
|
|
456ca8cc46 | ||
|
|
10fa81e77c | ||
|
|
b015b804a1 | ||
|
|
c5fd581a62 | ||
|
|
613a7429b8 | ||
|
|
9010677c6d | ||
|
|
2b00876e6b | ||
|
|
a87e4c7472 | ||
|
|
ebc074c308 | ||
|
|
e3325bb620 | ||
|
|
0bcb3829c7 | ||
|
|
848a44276f | ||
|
|
299be2364e | ||
|
|
a9b399ebbd | ||
|
|
87e2e27e56 | ||
|
|
4f2d320553 | ||
|
|
425c982865 | ||
|
|
804fbf7b05 | ||
|
|
111b354a58 | ||
|
|
7b4c6b42ae | ||
|
|
d21abaad3f | ||
|
|
e7670e65b3 | ||
|
|
c0e59a58c5 | ||
|
|
4f7e5c8259 | ||
|
|
2b93d00833 | ||
|
|
1b95e0e82a | ||
|
|
cfbc6bc83c | ||
|
|
23df273ae6 | ||
|
|
f00127f3f4 | ||
|
|
6c1c58e641 | ||
|
|
1b46ab0811 | ||
|
|
dab2340c19 | ||
|
|
959710675f | ||
|
|
6057a904ca | ||
|
|
16163eb7cd | ||
|
|
489de89e85 | ||
|
|
765c8234e3 | ||
|
|
697d6e9014 | ||
|
|
985e2c86ef | ||
|
|
5779539c94 | ||
|
|
7f5a453f26 | ||
|
|
0ef1f3d61e | ||
|
|
384c847a90 | ||
|
|
a091a96b69 | ||
|
|
366f8cd2bf | ||
|
|
fd78f0b177 | ||
|
|
8572830c69 | ||
|
|
8a045a4879 | ||
|
|
85faea9ea5 | ||
|
|
0a1ab112ee | ||
|
|
6846ff5ad1 | ||
|
|
e0e581c1b8 | ||
|
|
65808ccf58 | ||
|
|
d6d2d1157b | ||
|
|
f5c46b23b9 | ||
|
|
db90287c1f | ||
|
|
33e0daee77 | ||
|
|
7276966af9 | ||
|
|
1fe2efca4d | ||
|
|
f6346c27bc | ||
|
|
0b693fbcb7 | ||
|
|
ad4016f6d0 | ||
|
|
78d6328af8 | ||
|
|
48c3a4ee63 | ||
|
|
0f5411ab46 | ||
|
|
14b268c796 | ||
|
|
e9c0b6e458 | ||
|
|
f6f9e96c26 | ||
|
|
ca24134c6e | ||
|
|
8163dc96ca | ||
|
|
2eb5d81e74 | ||
|
|
4700ec8098 | ||
|
|
47325b387e | ||
|
|
1d04a5b7b1 | ||
|
|
0e2b9f136a | ||
|
|
a37dc2feef | ||
|
|
7a2551f71f | ||
|
|
3a3973744f | ||
|
|
66831fe655 | ||
|
|
484f219270 | ||
|
|
4636520cb4 | ||
|
|
64d57e3263 | ||
|
|
07a78fcc26 | ||
|
|
b5fc95bfed | ||
|
|
f28dd40242 | ||
|
|
423f7c1c12 | ||
|
|
76f0b9315a | ||
|
|
41695edc45 | ||
|
|
2685c82ec6 | ||
|
|
ba2fcf6ad8 | ||
|
|
e35d20e57e | ||
|
|
61c37e7fb3 | ||
|
|
fd8ea88d31 | ||
|
|
8daacfcf9a | ||
|
|
24f030591b | ||
|
|
3719cc70e7 | ||
|
|
53fb99f8f2 | ||
|
|
830722904b | ||
|
|
3dec406733 | ||
|
|
0b68d071a1 | ||
|
|
9cae140cb1 | ||
|
|
d43f46ceb9 | ||
|
|
1c400d6685 | ||
|
|
ac910efbda | ||
|
|
a1e1a123f3 | ||
|
|
4c4032b5ad | ||
|
|
9c5a66b80e | ||
|
|
1ba99def41 | ||
|
|
eeca03ae61 | ||
|
|
3aa7ba1629 | ||
|
|
ea06f4940a | ||
|
|
e9b89b377a | ||
|
|
eb1a592abe | ||
|
|
797a4d6afd | ||
|
|
a0ab248479 | ||
|
|
6b7a089057 | ||
|
|
c634d46b06 | ||
|
|
6d7f6cbe2b | ||
|
|
0673ab6ce7 | ||
|
|
87441ab0ec | ||
|
|
c8bc0e7328 | ||
|
|
00dfb87c17 | ||
|
|
64187bd254 | ||
|
|
2173737132 | ||
|
|
b32573f9b7 | ||
|
|
797ffeff2a | ||
|
|
26a70a8398 | ||
|
|
fe4e67b953 | ||
|
|
3d481e5e47 | ||
|
|
6e60cff400 | ||
|
|
4f196eb27d | ||
|
|
54b07d1e43 | ||
|
|
d7bbf20585 | ||
|
|
6f80f5f4ad | ||
|
|
aacca45da6 | ||
|
|
d0f95923a3 | ||
|
|
91ab867792 | ||
|
|
0654558b6b | ||
|
|
be756b3fe9 | ||
|
|
6202c0c462 | ||
|
|
d92d346de3 | ||
|
|
7f686c28c6 | ||
|
|
dba8663e18 | ||
|
|
f0b4070ae2 | ||
|
|
ab550a2831 | ||
|
|
7b836c2894 | ||
|
|
b7dbac0695 | ||
|
|
04e3f5b522 | ||
|
|
ffd02c787d | ||
|
|
c3662f6bc5 | ||
|
|
5d3af7e3cb | ||
|
|
ae01864e0d | ||
|
|
0e9dcc2b1f | ||
|
|
28b3301063 | ||
|
|
c4912ffd8f | ||
|
|
0fe7584f4b | ||
|
|
6469e3de36 | ||
|
|
6924cc1799 | ||
|
|
1378543bf7 | ||
|
|
cf6b7b4d24 | ||
|
|
9409261f3b | ||
|
|
3d7dca1f43 | ||
|
|
5cd5c39916 | ||
|
|
b0ab6bd302 | ||
|
|
7dad19f8be | ||
|
|
681cf01bd4 | ||
|
|
403d890140 | ||
|
|
0fcd8a45b6 | ||
|
|
a63dd4a846 | ||
|
|
cdd4cecb5b | ||
|
|
57a650cf6b | ||
|
|
e39ba6e62f | ||
|
|
59b1219128 | ||
|
|
244ec6626f | ||
|
|
d797f57751 | ||
|
|
bc2c700ca5 | ||
|
|
bc8ba16b6b | ||
|
|
640297867f | ||
|
|
cf3aa9f0cf | ||
|
|
2663c36dc2 | ||
|
|
1179d9330a | ||
|
|
d0b401d3cf | ||
|
|
aa6bc36b89 | ||
|
|
19dc8f2704 | ||
|
|
939b40b014 | ||
|
|
f1ece3a89e | ||
|
|
97ebca5046 | ||
|
|
20a80933b5 | ||
|
|
adf3b6b663 | ||
|
|
b546929c9c | ||
|
|
c5b515bb35 | ||
|
|
223575c232 | ||
|
|
466139fadf | ||
|
|
cfc882f303 | ||
|
|
717861057f | ||
|
|
4841bfc690 | ||
|
|
b8fe0bca90 | ||
|
|
072d9e3538 | ||
|
|
2eb371fcd6 | ||
|
|
190b040b1a | ||
|
|
3c9904ddd5 | ||
|
|
27597e9a5a | ||
|
|
3b29c0613a | ||
|
|
6b4f1b91d5 | ||
|
|
039b5540a9 | ||
|
|
463fddefd3 | ||
|
|
6da2e35e5b | ||
|
|
d003283939 | ||
|
|
f560322547 | ||
|
|
e09663aa71 | ||
|
|
93f8a0a4d3 | ||
|
|
ff16ae33c5 | ||
|
|
b8a3f81859 | ||
|
|
d0c6bef6b5 | ||
|
|
355b8edd86 | ||
|
|
bbe8ac9742 | ||
|
|
b9c9e8af98 | ||
|
|
820a3cb4d0 | ||
|
|
aaa1d7f0a4 | ||
|
|
ea6a0c4d5f | ||
|
|
dbdce02e79 | ||
|
|
99b31ba9a2 | ||
|
|
f84fb52609 | ||
|
|
a035b9dbb0 | ||
|
|
96223eee10 | ||
|
|
ce015babec | ||
|
|
3dfe705da1 | ||
|
|
13c1a2a0b0 | ||
|
|
eefbe0f7c1 | ||
|
|
53123e05e0 | ||
|
|
54835866a7 | ||
|
|
8f3ed70a57 | ||
|
|
6b7c0e4ac9 | ||
|
|
40cc4a3d37 | ||
|
|
a960b837df | ||
|
|
47342ee739 | ||
|
|
435c4e0ed4 | ||
|
|
597b613e2d | ||
|
|
7f51609b72 | ||
|
|
8f2484e736 | ||
|
|
8a32910084 | ||
|
|
6b30ac27ea | ||
|
|
51c4dd05ae | ||
|
|
98f53cddc8 | ||
|
|
25d8815c17 | ||
|
|
8220f502e6 | ||
|
|
129f2d97d6 | ||
|
|
1ea03ac015 | ||
|
|
f02323561c | ||
|
|
050b0bc55a | ||
|
|
ca7c145457 | ||
|
|
668dff4620 | ||
|
|
85560cc438 | ||
|
|
d3f293f622 | ||
|
|
9922526fc6 | ||
|
|
4cdfc97a9f | ||
|
|
4bcc5c5b38 | ||
|
|
cfe897c3a3 | ||
|
|
b6491f9d6a | ||
|
|
d3d042e539 | ||
|
|
26a3a2abb6 | ||
|
|
d98b55c7ff | ||
|
|
a0ebd52c0a | ||
|
|
d74f501f4c | ||
|
|
ee2d53f10b | ||
|
|
ad64637a0d | ||
|
|
7abcd054aa | ||
|
|
3dd0740222 | ||
|
|
185d421074 | ||
|
|
a94d8bfbc3 | ||
|
|
29b23cc862 | ||
|
|
18ca33e9d2 | ||
|
|
4e4aa23552 | ||
|
|
32d45c9097 | ||
|
|
b107237a90 | ||
|
|
0ff7c16d0d | ||
|
|
19c51fa721 | ||
|
|
7319cc7cc9 | ||
|
|
29cc0d9a11 | ||
|
|
977213c70e | ||
|
|
fd4b9c2ab1 | ||
|
|
81ef952451 | ||
|
|
a3748a6ae8 | ||
|
|
6dd46ca19f | ||
|
|
ef62fbe44a | ||
|
|
db18d9ad81 | ||
|
|
019d0994c8 | ||
|
|
f9ca725899 | ||
|
|
cfde7dc6a8 | ||
|
|
0630b0e078 | ||
|
|
d5a8b13b0e | ||
|
|
bea47cc003 | ||
|
|
28d354b90a | ||
|
|
10140556e6 | ||
|
|
63ee7e17b8 | ||
|
|
84c8aa4129 | ||
|
|
460046c732 | ||
|
|
239f2d0269 | ||
|
|
0f89a490de | ||
|
|
b8ebbcbb6c | ||
|
|
8088c32218 | ||
|
|
912f66883a | ||
|
|
fd4ba25f1b | ||
|
|
d4706a62bd | ||
|
|
df859686bf | ||
|
|
9a651e3725 | ||
|
|
5dd9cd5831 | ||
|
|
c7658527d5 | ||
|
|
cd3ead7ac2 | ||
|
|
d8dca25a0e | ||
|
|
836dae0c0e | ||
|
|
da4c9e6331 | ||
|
|
d0e6f325c4 | ||
|
|
b31bb1c893 | ||
|
|
a7009beed5 | ||
|
|
354c117624 | ||
|
|
e95b6a3824 | ||
|
|
88037fe297 | ||
|
|
92219b4740 | ||
|
|
fb34d74e14 | ||
|
|
0b8a163d0c | ||
|
|
b267eb92e9 | ||
|
|
0e8c7ec7a8 | ||
|
|
0d5a05e9da | ||
|
|
11537f8cb6 | ||
|
|
294fba97d9 | ||
|
|
02294acbfc | ||
|
|
bbd337fa1f | ||
|
|
3c64d2ebcf | ||
|
|
b7b4d94b30 | ||
|
|
de7a86dfa5 | ||
|
|
c009038d6d | ||
|
|
ee70abbfbe | ||
|
|
0a8147cee2 | ||
|
|
722794c425 | ||
|
|
75d31e39fe | ||
|
|
033c473199 | ||
|
|
52c6dfed5b | ||
|
|
0eb493b865 | ||
|
|
ac32050e1f | ||
|
|
64bd9ba86f | ||
|
|
c2f40a8d50 | ||
|
|
10f2dad358 | ||
|
|
3af63d929d | ||
|
|
2e0f63bf25 | ||
|
|
3f35c9484c | ||
|
|
213db219c6 | ||
|
|
2803619cc8 | ||
|
|
76abd54689 | ||
|
|
db5723f86d | ||
|
|
cae3f2dec3 | ||
|
|
f709edb2ee | ||
|
|
c90f74d492 | ||
|
|
7a8f9289ad | ||
|
|
22fa8d28bc | ||
|
|
9b99e47b4b | ||
|
|
1150f9cf6b | ||
|
|
5dc63a0413 | ||
|
|
2dc379e378 | ||
|
|
49af54175b | ||
|
|
29555fec94 | ||
|
|
5e5465c02a | ||
|
|
8f55db2279 | ||
|
|
5d52904f76 | ||
|
|
5e1c531cbe | ||
|
|
1f7c1827f4 | ||
|
|
65297c3423 | ||
|
|
f23c671f69 | ||
|
|
c3697964be | ||
|
|
1d2485db3c | ||
|
|
45a3729ae1 | ||
|
|
efe07135b9 | ||
|
|
a002e784ba | ||
|
|
7120ec60b9 | ||
|
|
7a23639eaf | ||
|
|
b2fd729051 | ||
|
|
150bab35b5 | ||
|
|
e56afdcea2 | ||
|
|
b157980d8a | ||
|
|
73438f493b | ||
|
|
b490a44abd | ||
|
|
cc18ece654 | ||
|
|
4d9cb5f98b | ||
|
|
3a51868d2d | ||
|
|
2434d94313 | ||
|
|
dcc41044e3 | ||
|
|
8215b16b36 | ||
|
|
f1f4359aeb | ||
|
|
98cc0528ab | ||
|
|
64a77f5ddb | ||
|
|
c024d06806 | ||
|
|
7c940010fd | ||
|
|
15914ebae7 | ||
|
|
259bbfd519 | ||
|
|
7865839761 | ||
|
|
36180d7851 | ||
|
|
9833b53a34 | ||
|
|
b83847c241 | ||
|
|
a27807b046 | ||
|
|
17167978a2 | ||
|
|
873b5eebee | ||
|
|
e53244931a | ||
|
|
90bec36d01 | ||
|
|
8cea43e57a | ||
|
|
963f14bb8a | ||
|
|
a7ebcfe053 | ||
|
|
9448972d45 | ||
|
|
3be86cdd96 | ||
|
|
92e34204ca | ||
|
|
bc78b1606b | ||
|
|
25796fc190 | ||
|
|
309ef93512 | ||
|
|
34d5a1db4e | ||
|
|
68a1be0800 | ||
|
|
698892e3e7 | ||
|
|
52b35f7e0d | ||
|
|
63bb6e3a9a | ||
|
|
5e419e8f09 | ||
|
|
60977c9306 | ||
|
|
8a37009790 | ||
|
|
7a8043ea8a | ||
|
|
28f7f9752f | ||
|
|
21d01e9379 | ||
|
|
dad84feb38 | ||
|
|
f7f18a99f2 | ||
|
|
2abf0aefab | ||
|
|
09fce959d4 | ||
|
|
a9465c854a | ||
|
|
812fd9efbb | ||
|
|
15d07f98d7 | ||
|
|
8bd22fc906 | ||
|
|
89870c7e3f | ||
|
|
02720f190b | ||
|
|
e2982224b3 | ||
|
|
a1c5e43f8e | ||
|
|
6fbf7a36f1 | ||
|
|
71259f6708 | ||
|
|
b2616a856b | ||
|
|
e4502ea55c | ||
|
|
07eed9f6e1 | ||
|
|
7167d36f7a | ||
|
|
ee8d66ba41 | ||
|
|
18b3da9c1e | ||
|
|
7cc64d6aa2 | ||
|
|
a41f4a6639 | ||
|
|
80fe8e50aa | ||
|
|
6e5eb1d91a | ||
|
|
1e49da939d | ||
|
|
2757a64628 | ||
|
|
30c7dfcb65 | ||
|
|
4a6bf81d47 | ||
|
|
dcb2c2dc40 | ||
|
|
bf1f5892cf | ||
|
|
e6b917c37c | ||
|
|
058e58a7b7 | ||
|
|
5e548798a5 | ||
|
|
48bc514f5b | ||
|
|
2dd2fd0928 | ||
|
|
c5b28df758 | ||
|
|
8290f1e276 | ||
|
|
cdaa921880 | ||
|
|
035986ea4f | ||
|
|
3871b7bd4f | ||
|
|
67f8a8d162 | ||
|
|
e29c83b8e7 | ||
|
|
46e016d55e | ||
|
|
690ffea6c5 | ||
|
|
2f09981ea0 | ||
|
|
cc00c92d42 | ||
|
|
03bde72f62 | ||
|
|
9d858bfe39 | ||
|
|
c5e81161fa | ||
|
|
f57b362c7a | ||
|
|
ff2839fb29 | ||
|
|
c39f9c378b | ||
|
|
f000415b8e | ||
|
|
0df50c337b | ||
|
|
d1491f0421 | ||
|
|
cee6408979 | ||
|
|
3268a9ed03 | ||
|
|
88c2437a01 | ||
|
|
2447a4be45 | ||
|
|
fa9f84e5ec | ||
|
|
49a9459a35 | ||
|
|
500290db7f | ||
|
|
8e6a28eaf0 | ||
|
|
e16b8a0729 | ||
|
|
bc3ef19666 | ||
|
|
0aacc165bf | ||
|
|
ebef40fde8 | ||
|
|
1783b7f407 | ||
|
|
3fe1fe869f | ||
|
|
2b3de6244d | ||
|
|
78f4132803 | ||
|
|
93a5a75f3f | ||
|
|
6bcc60d859 | ||
|
|
5c096eb876 | ||
|
|
d5215656fb | ||
|
|
2df158ecf3 | ||
|
|
e1e88d1d87 | ||
|
|
5cc81ac706 | ||
|
|
ffa6634814 | ||
|
|
4e0c24a98b | ||
|
|
7a275bfe55 | ||
|
|
7fd8843a8b | ||
|
|
83d9d48ffe | ||
|
|
66b35d4408 | ||
|
|
345ed64bb1 | ||
|
|
f8cc82975d | ||
|
|
65f2ef0e74 | ||
|
|
6e931b6006 | ||
|
|
e2c108332f | ||
|
|
f518e508ca | ||
|
|
7db52d1287 | ||
|
|
3e57f1e33e | ||
|
|
d0a1687c58 | ||
|
|
498e1176f1 | ||
|
|
9ac4b7f047 | ||
|
|
f534c28b7e | ||
|
|
3fe5193ea4 | ||
|
|
d322c0a148 | ||
|
|
39edbf69e2 | ||
|
|
b180adad32 | ||
|
|
bd0f2e1be3 | ||
|
|
ce95e8cc56 | ||
|
|
e80eb4cd25 | ||
|
|
e9615e346a | ||
|
|
b598240338 | ||
|
|
f73e5568de | ||
|
|
8115309712 | ||
|
|
b17ebee4fa | ||
|
|
5923084bb5 | ||
|
|
89fce1cfc0 | ||
|
|
7d5c878e6a | ||
|
|
06eb6323d9 | ||
|
|
66515e38f4 | ||
|
|
35bbe8e59a | ||
|
|
1a0793af8b | ||
|
|
b765541da0 | ||
|
|
fb90abed76 | ||
|
|
a4773969fb | ||
|
|
8aa3c269bd | ||
|
|
0b92a5bda2 | ||
|
|
70524ab31a | ||
|
|
20c39de353 | ||
|
|
0c0557050b | ||
|
|
bc704dc7c2 | ||
|
|
246ff507eb | ||
|
|
8ca9de3c8d | ||
|
|
64a1f1f41b | ||
|
|
3856746a7f | ||
|
|
504ad25036 | ||
|
|
289b42aaa0 | ||
|
|
25f4895017 | ||
|
|
8d418ab952 | ||
|
|
829515aaef | ||
|
|
2106a069a7 | ||
|
|
aba65ffaf7 | ||
|
|
0e171ee499 | ||
|
|
94c35502d9 | ||
|
|
7cd8d1d546 | ||
|
|
4949bce992 | ||
|
|
7a496a39df | ||
|
|
1b93d38872 | ||
|
|
52467b9dde | ||
|
|
87239c859e | ||
|
|
d689655ef5 | ||
|
|
af4a021bd4 | ||
|
|
33c16ff13c | ||
|
|
e835f27a3d | ||
|
|
f16ebc505b | ||
|
|
f6497be479 | ||
|
|
bb3d880cbb | ||
|
|
c8e56bce58 | ||
|
|
12043616ff | ||
|
|
94402469f4 | ||
|
|
95fc4d1835 | ||
|
|
dc22920518 | ||
|
|
6c9658bf29 | ||
|
|
3a8bda0188 | ||
|
|
9574ae114b | ||
|
|
1ef5e1e0d2 | ||
|
|
66b8742e0f | ||
|
|
20d8a8d10e | ||
|
|
bcf45c4113 | ||
|
|
89c5a98480 | ||
|
|
94c288b136 | ||
|
|
ac895da1c8 | ||
|
|
c8424b34e8 | ||
|
|
a063f55b08 | ||
|
|
7a023c2180 | ||
|
|
13f3f2728d | ||
|
|
c8aa988ecd | ||
|
|
b2c8fd08f4 | ||
|
|
7d9b8f58cf | ||
|
|
61e6123749 | ||
|
|
33a724e6f6 | ||
|
|
4184b39513 | ||
|
|
fae7e56ceb | ||
|
|
c37a20f091 | ||
|
|
c33e7efaa5 | ||
|
|
9d0e1acdd2 | ||
|
|
ef7faa40e1 | ||
|
|
5dcfdff59e | ||
|
|
6f624cfaa9 | ||
|
|
c8db2a8a7c | ||
|
|
649fa52e5d | ||
|
|
fad56bea19 | ||
|
|
c835a3bd27 | ||
|
|
f1c1047b1e | ||
|
|
8cffa7d4ac | ||
|
|
25954c6ad4 | ||
|
|
6da249e975 | ||
|
|
a24f920e57 | ||
|
|
f3222a70ed | ||
|
|
06019ad66d | ||
|
|
0800cd30f4 | ||
|
|
1bc2d87392 | ||
|
|
7ec242397c | ||
|
|
57535de494 | ||
|
|
ecb8ef1755 | ||
|
|
04fda1f91f | ||
|
|
493ea06ed1 | ||
|
|
d43f1b0a80 | ||
|
|
1cab4c8709 | ||
|
|
d310a295a6 | ||
|
|
83d294585e | ||
|
|
8f94cfb6b6 | ||
|
|
db842726b3 | ||
|
|
1409a06ae9 | ||
|
|
77c04dd28b | ||
|
|
8acfe6930d | ||
|
|
273e374846 | ||
|
|
18e68f0a06 | ||
|
|
5e002ffca2 | ||
|
|
317c2834c6 | ||
|
|
cad73bc3d5 | ||
|
|
d9ffac81d3 | ||
|
|
bdbdb4d2b0 | ||
|
|
5be95976d6 | ||
|
|
38e3fac70e | ||
|
|
0fd56e7329 | ||
|
|
bd7662d746 | ||
|
|
85d00d256c | ||
|
|
9d22dea6fd | ||
|
|
c0ece5248d | ||
|
|
318433b6ab | ||
|
|
b8d3bcd30f | ||
|
|
85235ac96b | ||
|
|
9a368bc8a0 | ||
|
|
394182b937 | ||
|
|
be1c9c66f0 | ||
|
|
1fc5688685 | ||
|
|
3e802ea1e5 | ||
|
|
1e9e728ec7 | ||
|
|
29606dba1c | ||
|
|
d7e6111a82 | ||
|
|
270dbc42c1 | ||
|
|
8c4e876e2d | ||
|
|
a438ab7166 | ||
|
|
99730e66a0 | ||
|
|
1156cc1ffb | ||
|
|
4287ccf7a6 | ||
|
|
726a70c97e | ||
|
|
964185fdcc | ||
|
|
d7109a8648 | ||
|
|
8d3f35fb40 | ||
|
|
509c370efc | ||
|
|
771a4ca441 | ||
|
|
7656cd6000 | ||
|
|
b8ae06f38e | ||
|
|
75ce020cb5 | ||
|
|
a0e10b116a | ||
|
|
44bda08834 | ||
|
|
3b10682041 | ||
|
|
fb0da0b431 | ||
|
|
90aaf6fa0c | ||
|
|
f9edfa0065 | ||
|
|
7c176516e6 | ||
|
|
5dcfea12db | ||
|
|
f19c588e0f | ||
|
|
3fc0d8d3af | ||
|
|
a17522fa36 | ||
|
|
c0d29fe009 | ||
|
|
56ed58082c | ||
|
|
9c0d214c3c | ||
|
|
8cf89b69da | ||
|
|
4fe68647f2 | ||
|
|
95b026571f | ||
|
|
e632fbce12 | ||
|
|
a14d28af07 | ||
|
|
f4b5d83e96 | ||
|
|
aca52a4de4 | ||
|
|
fba959c6aa | ||
|
|
b81dbc09f8 | ||
|
|
ce56d4b63f | ||
|
|
cfe5a32ebc | ||
|
|
f4f2dcb2d3 | ||
|
|
5c2326a952 | ||
|
|
c299188720 | ||
|
|
6a118f2514 | ||
|
|
8e69010364 | ||
|
|
5795e98eb2 | ||
|
|
944509d03a | ||
|
|
12ff5d4301 | ||
|
|
995332b1cf | ||
|
|
d9c30cd23a | ||
|
|
7bf07bf690 | ||
|
|
60c7793082 | ||
|
|
d1d5019cc2 | ||
|
|
643d0f21d4 | ||
|
|
4314108f56 | ||
|
|
89037fcd5c | ||
|
|
a5a861cc18 | ||
|
|
95665993c6 | ||
|
|
9a10dcb499 | ||
|
|
702aa2905a | ||
|
|
d810c7a27c | ||
|
|
eac62b5250 | ||
|
|
33e8ae435b | ||
|
|
96a7084241 | ||
|
|
3e9636f69b | ||
|
|
be25253ee7 | ||
|
|
c0bd129b5c | ||
|
|
5e0c8f6c13 | ||
|
|
07df8e8074 | ||
|
|
8450973c0a | ||
|
|
6e24927d97 | ||
|
|
2eeca8d9a1 | ||
|
|
7c2148e901 | ||
|
|
143241719a | ||
|
|
ef286355db | ||
|
|
5a4e4ded65 | ||
|
|
f51271521f | ||
|
|
1992a95d51 | ||
|
|
a1f8451ee6 | ||
|
|
70dcc38503 | ||
|
|
df0e2c17c4 | ||
|
|
d336f3f365 | ||
|
|
1c70610a06 | ||
|
|
a733373a70 | ||
|
|
61f2a96547 | ||
|
|
9e0bff9e2a | ||
|
|
531edc547b | ||
|
|
dc1f13a04e | ||
|
|
84e1941589 | ||
|
|
7cac85405a | ||
|
|
fd26b04b1c | ||
|
|
f39fd40339 | ||
|
|
31d1553deb | ||
|
|
96593d708f | ||
|
|
e2b4f67561 | ||
|
|
71ca94d5b3 | ||
|
|
92bf6228a7 | ||
|
|
ee3dd12f80 | ||
|
|
9d7bf0e95c | ||
|
|
92e1e68b3a | ||
|
|
74caac5d8a | ||
|
|
2b68dbe2c8 | ||
|
|
b3dc99ae8b | ||
|
|
ca13ba1a67 | ||
|
|
07d3051107 | ||
|
|
0f71576bb3 | ||
|
|
8b7eb04779 | ||
|
|
274d16b6ad | ||
|
|
4725f24406 | ||
|
|
34ca820f07 | ||
|
|
76f95c5eb6 | ||
|
|
1979a9f55d | ||
|
|
394be2b2dd | ||
|
|
c0413f1fd9 | ||
|
|
bf1eb887bb | ||
|
|
35f3ca2fd4 | ||
|
|
849b16a6f3 | ||
|
|
f614a3f0d4 | ||
|
|
95d648d474 | ||
|
|
4a76e10751 | ||
|
|
4426a37dfd | ||
|
|
51ca27340e | ||
|
|
a2c91e2149 | ||
|
|
361d689f0c | ||
|
|
fb837c8a99 | ||
|
|
6563456d57 | ||
|
|
08ce6e4dcd | ||
|
|
d5f0ab39d8 | ||
|
|
f9490862dc | ||
|
|
42eef62fa2 | ||
|
|
f8a78c8238 | ||
|
|
c3ae115898 | ||
|
|
6ff5d515ac | ||
|
|
63ac7497af | ||
|
|
3d2eec6ef7 | ||
|
|
dc85025932 | ||
|
|
4953ed2e68 | ||
|
|
645285bfe6 | ||
|
|
b5a6b211d8 | ||
|
|
478353c8f8 | ||
|
|
7d72d6c755 | ||
|
|
333700073b | ||
|
|
9795c120b8 | ||
|
|
ed28fe911f | ||
|
|
3ee11dd907 | ||
|
|
d85c59e9ba | ||
|
|
84986155dd | ||
|
|
8fe443ba69 | ||
|
|
8e6d43283b | ||
|
|
f9f899a636 | ||
|
|
78d41402f1 | ||
|
|
0bc3b86d8d | ||
|
|
7454ee8e3e | ||
|
|
60664a56f2 | ||
|
|
3497b89fec | ||
|
|
af068b3810 | ||
|
|
79482b4da2 | ||
|
|
e80e6b9431 | ||
|
|
2d9efbeeb6 | ||
|
|
2b4a96c9cc | ||
|
|
2833029d65 | ||
|
|
ee0a5f7363 | ||
|
|
5834ccdf9d | ||
|
|
00467b6a01 | ||
|
|
9fafc203f5 | ||
|
|
f1c4748ba4 | ||
|
|
ed349f0697 | ||
|
|
4138f1b1e3 | ||
|
|
f09172490a | ||
|
|
97854c84db | ||
|
|
023910ce34 | ||
|
|
5d771913dd | ||
|
|
52ea6fa92d | ||
|
|
73408135be | ||
|
|
549f791d3c | ||
|
|
24d229edf5 | ||
|
|
19cf5fe9a0 | ||
|
|
0215a6a516 | ||
|
|
3a8f75bb95 | ||
|
|
0b7dc23014 | ||
|
|
964395040f | ||
|
|
d1660355eb | ||
|
|
f57643f732 | ||
|
|
427dfa23e8 | ||
|
|
88d35436ad | ||
|
|
641f24e0c9 | ||
|
|
1ed6c956eb | ||
|
|
5415677034 | ||
|
|
7fcad33eb3 | ||
|
|
af33a91941 | ||
|
|
5c5f2dcc51 | ||
|
|
d363da17fc | ||
|
|
54a5cdd9a1 | ||
|
|
31360e9845 | ||
|
|
d144c90b04 | ||
|
|
effe833971 | ||
|
|
a7dddf277c | ||
|
|
2f443441f5 | ||
|
|
ea1c5f31c5 | ||
|
|
0a32b8f5bd | ||
|
|
db1edf162f | ||
|
|
17891f5579 | ||
|
|
885864fc1e | ||
|
|
2718aa5a1c | ||
|
|
3b915d736c | ||
|
|
b2cc03be79 | ||
|
|
ac3d305279 | ||
|
|
dd08cb950f | ||
|
|
15592a01af | ||
|
|
ee6e4ad50d | ||
|
|
65dc06b980 | ||
|
|
e81ab56a4a | ||
|
|
8d079410ea | ||
|
|
31c100d9bc | ||
|
|
b7a9b5c77c | ||
|
|
5f25da5d5d | ||
|
|
a53d139687 | ||
|
|
5d82df9983 | ||
|
|
9f72bd78f8 | ||
|
|
51408302a9 | ||
|
|
1302c1b455 | ||
|
|
83495a7466 | ||
|
|
0c83b39b8b | ||
|
|
0da172106b | ||
|
|
2db9c50aa5 | ||
|
|
718e70f08c | ||
|
|
e8d8531304 | ||
|
|
aab19a383e | ||
|
|
677d6732b0 | ||
|
|
24bde55bb3 | ||
|
|
c89824f83a | ||
|
|
0537706b9c | ||
|
|
b5c3d1e39d | ||
|
|
095096e325 | ||
|
|
876d2a2f27 | ||
|
|
67823cfc1d | ||
|
|
fe83a9c75a | ||
|
|
86c83311c7 | ||
|
|
d45490ccaf | ||
|
|
df1ac952df | ||
|
|
46182edb34 | ||
|
|
4b84f35d59 | ||
|
|
3631df4619 | ||
|
|
a5ba313375 | ||
|
|
5f5322885a | ||
|
|
eff473ed48 | ||
|
|
212481f44f | ||
|
|
afc46ac7a7 | ||
|
|
f4b055bda2 | ||
|
|
f9c001230e | ||
|
|
f08500b61b | ||
|
|
bcd9527696 | ||
|
|
46b5306aef | ||
|
|
7dd6dc14a4 | ||
|
|
5fe11a1475 | ||
|
|
8b433d732f | ||
|
|
eeae76a06f | ||
|
|
f0a72b7961 | ||
|
|
63fc8f0468 | ||
|
|
bb83b90ae0 | ||
|
|
90bb0e70ab | ||
|
|
3667d586c4 | ||
|
|
ea5fdb1a9e | ||
|
|
b4355cd0ba | ||
|
|
5248005e7a | ||
|
|
0535356ee9 | ||
|
|
034aa78842 | ||
|
|
2df87f4f16 | ||
|
|
4706a3d425 | ||
|
|
6490fb8707 | ||
|
|
3193f28c69 | ||
|
|
37a3aaf0c0 | ||
|
|
b29c95eb31 | ||
|
|
9c325c8cfc | ||
|
|
0be1856449 | ||
|
|
ef662efa80 | ||
|
|
c2020218c2 | ||
|
|
93e1dcd1c1 | ||
|
|
5bcee3c5c4 | ||
|
|
533843caea | ||
|
|
e44a7a3ec4 | ||
|
|
d11700f8c4 | ||
|
|
6ba0a5c712 | ||
|
|
c4d31645ce | ||
|
|
9041ed9a0c | ||
|
|
372e6b887a | ||
|
|
d828e2c1aa | ||
|
|
cefa7889f8 | ||
|
|
174cfd6287 | ||
|
|
0948ca9168 | ||
|
|
6dd9539410 | ||
|
|
c0a197c3f4 | ||
|
|
8f7d43186b | ||
|
|
b7da8ac00b | ||
|
|
f684d446e7 | ||
|
|
52feb78eb2 | ||
|
|
aa8e59aefa | ||
|
|
bc947d2407 | ||
|
|
11f5fe79cf | ||
|
|
605a06a7ec | ||
|
|
b292a88650 | ||
|
|
b9e43535a9 | ||
|
|
54303e49eb | ||
|
|
52c8a2d393 | ||
|
|
a398e95612 | ||
|
|
66fc7493c1 | ||
|
|
0810cd4ae0 | ||
|
|
5fe11d1fdb | ||
|
|
7bac83cac6 | ||
|
|
407a6d67cd | ||
|
|
bcef03b362 | ||
|
|
ac1c297eaa | ||
|
|
5472ff127b | ||
|
|
03f5c9c90f | ||
|
|
f56d0e5542 | ||
|
|
25446702df | ||
|
|
693553627d | ||
|
|
0e5e8f5cc4 | ||
|
|
be109653e8 | ||
|
|
24f5d29071 | ||
|
|
d7383009d4 | ||
|
|
c426739233 | ||
|
|
3437622b95 | ||
|
|
5cfaaf70a5 | ||
|
|
423059f11a | ||
|
|
cebc18b776 | ||
|
|
51e77d6344 | ||
|
|
181fcbf6c4 | ||
|
|
19a47bccec | ||
|
|
25c27c66c7 | ||
|
|
946bc87198 | ||
|
|
ec990d2dd1 | ||
|
|
57aa62d68e | ||
|
|
fc211daf25 | ||
|
|
70c76ee8ae | ||
|
|
6cac3f94a2 | ||
|
|
f2e2a5ba1a | ||
|
|
7d141c5b23 | ||
|
|
ffa9a5830f | ||
|
|
e40a975ac3 | ||
|
|
d31d57a2ca | ||
|
|
871f0feac1 | ||
|
|
94ba0e5470 | ||
|
|
c94ef48844 | ||
|
|
e3463907f6 | ||
|
|
9d8a9c04ad | ||
|
|
9bc52de57d |
@ -1,5 +0,0 @@
|
|||||||
PMRA Document Number,GAP Active Ingredient,Regulatory Utility,Document Comment,SIC Number,Protocol Number,Protocol Number,Data Evaluation Record,Audience,Original Retirement Date,Regulatory Released,Regulatory Authority,Regulatory Authority (legacy to remove),Source Owner,Legacy Primary OECD Code,Legacy Source Organisation,Regulator Comments,Contains Registered Composition?,File Name,FTP Source Location,Submission Format Needed,Legacy Report Number,Sales Unit,Address,ANVISA Process Number,Source Number,Legacy Migration Path,Review Completion Date,Registration Number,Agency Dossier Registration Type,Submission Version,Media Comments,Literature Citation,Document Language,Notes,Study Location,Materials,Approver,Subtype,Test Facility,Is Latest Version,Consumer,Classification,Guideline,TRP Type,Legacy System Date,Function,Artist,Document Title,PI Number,Legacy Version Description,PI Number,Incoming from,Annotations (Unresolved),Work To Date,Copyright Clearance Obtained?,Summary Type,Annotations (Claim Links),Latest Source Minor Version,Regulatory Category,SYN Letter Number,Work From Date,Product,Version Creation Date,Governance Committee,Test Facility (legacy to remove),Regulatory Finalization Date,Imported file?,Status,Information Tracking Submission Package,Approval Type,Media Description,Duration,Legacy System,Archive Date,Exporting country,Regulatory Tracking Number,Annotations (Anchors),Information Tracking,Document Number,GAP Usage Information,Report Number,Legacy Reg Document Number,Legal Representative 2,Registering Company,File Created By,Legacy EPA Decision Code,Meeting Minutes,Product Safety Finalization Date,Report Type,Archived Date,Legacy Other PMRA Data Codes,Template Document Type,Color Space,Legal Representative 1,Document Author,CrossLink,Annotations (Notes),Suggested Links,Assessment Type,Legacy Species Commodity,TK Number,Legacy Test Facilities,Planned Completion Date,PRF Number,Registration Item Country,Legacy Target Species,Legacy Owning Organisation,Archive Number,Protocol Type,Submission Output Format,Submission Date,Coordinator,Regulatory Reporting Date,Annotations (Lines),Field Trial Number,Batch Number,Legacy Version Number,External ID,Author Names,Sponsor Organisation (legacy to remove),Literature Type,Sponsor Organisation,Legacy Crop Host,Document Edition,Agreement Expiry Date,TRP Usage,Legacy EPA Submission Type,Security Classification,Source Document Name,Syngenta Address,Outgoing to (legacy to remove),Link Status,Legacy System Document ID,Copyright,PMRA Registration Number,Annotations (All),SU,Source Vault Name,Latest Source Major Version,Legacy Other OECD Codes,Version Created By,Subarea,Reviewer,Owning Organisation (legacy to remove),Project,Viewer,Regulatory Territory,Lifecycle,Claims,Bound Source Minor Version,Global ID,Legacy Regulatory Authorities,Outgoing to,Additional Information,Year,Organism,File Last Modified By,Requires Attachment of a Signature Page?,Source Binding Rule,Destination country,Cross-Reference Submission Number,Name,Issue Topic,Pages,Type,Reason for Retiring,Rule,Type of Review,Original Finalization Date,Trade Name,Transmittal Document,Pages OCR'ed (%),Legacy Object ID(s),Protocol Number,Last Modified By,Annotations (Approved Links),Request Type,Binder,Author,Last Modified Date,Non-English Document Title,Media Location,Created By,Editor,Owner,CRO Report Number,Format,Legacy Discipline,Major Version Number,Created From,Active Ingredients,Archive Location,EPA Registration Number,Retirement Date,Annotations (Links),TRP Document,Finalization Date,Legacy Task Number,Legal Details,OCR Requested,Global Version ID,Publicly Published,Document Date,MRID Number,Legacy Reg Topic,Requires Review and Approval?,Annotations (Auto Links),Subareas,Product Safety Reporting Date,Media Title,Minor Version Number,Legacy Primary PMRA Data Code,Requestor,Owning Organisation,Zone,File Last Modified Date,Contact Person,File Created Date,Amendment Justification,CDPR Number,Legacy EPA Submission Date,Supports Agency Dossier,Field Trial Year,Reason for Un-retiring,TRP Topic,Bound Source Major Version,Keywords,Legacy Author Name(s),Export File Name,Size,Source Link,This content is a translation?,Quality Assurance Standard,Legacy Fed Reg Number,Checksum,Created Date,Annotations (Resolved),User Task,Review Start Date,test list,Incoming from (legacy to remove),Rendition Profile,Path,URL
|
|
||||||
,,Country Specific,,,,,No,,,Yes,,,,,Syngenta,,,Lambda-cyhalothrin - EU AIR5 - Document A - Statement of the context in which the dossier is submit.doc,,,,,,,,,,,,,,,English,,,PP321,,Authority Form / Document,,TRUE,"All Internal Users, Syngenta Read Only Restricted, Syngenta Read Only Non Restricted",,,,16/07/2019,,,Lambda-cyhalothrin - EU AIR5 - Document A - Statement of the context in which the dossier is submitted,PI0015818,,,,0,,,,0,,,,,,26/03/2020 14:06 CET,,Syngenta Crop Protection AG (Switzerland),,No,Final,,,,,SmartDoc - EAME,,,,0,IT-624180,VV-731623,,N/A,PP321_12074,,,,,,,,,,,,,B Sochard,No,0,0,,,,,,,,,,,,,,,,0,,,1.0|CURRENT,090100b881afdd91,,Syngenta Crop Protection AG (Switzerland),,Syngenta Crop Protection AG,,Original,,,,Internal Use Only,,,ETL_blank,,090100b881afdd91,,,0,,,,,Berangere Sochard,,,Syngenta Crop Protection AG (Switzerland),,,European Union,General Lifecycle,,,40037_850361,,ETL_blank,,,,,,,,,Lambda-cyhalothrin - EU AIR5 - Document A - Statement of the context in which the dossier is submit,,2,Registration Supporting Documentation,,,,,,No,,090100b881afdd91 (cv),,CP Vault Support,0,,No,,24/02/2021 15:20 CET,,,Vault Migration,"Global Reg Ops, NA Reg Ops, PS Ops, EAME Reg Ops, Syngenta Business Administrator, Berangere Sochard, Simon Baker, Lily Williams, Clive Boxwell, Paul Parsons, Richard MacKenzie, Dan Pickford, Laurence Hand",Vault Migration,,application/msword,Regulatory,2,,PP321,,,,0,No,,PI0015818,,No,40037_850361_1958105,No,01/09/2020,,Regulatory,No,0,,,,0,,,Syngenta Crop Protection AG,,,,,,,,,,,,,,Sochard Berangere,,49152,,No,,,86d75e5da397fdadcd332f92234a4f62,29/11/2019 08:51 CET,0,,,,,,Lambda-cyhalothrin - EU AIR5 - Document A - Statement of the.pdf,
|
|
||||||
,,Country Specific,,,,,No,,,Yes,,,,,Syngenta,,,Lambda-cyhalothrin - EU AIR5 - MCA Section 5 - Toxicology (1).docx,,,,,,,,,,,,,,,English,,,PP321,,Summary / Overview,,TRUE,"All Internal Users, Syngenta Read Only Non Restricted, Syngenta Read Only Restricted",,,,16/07/2019,,,Lambda-cyhalothrin - EU AIR5 - MCA Section 5 - Toxicology,PI0015818,,,,0,,,OECD Tier 2 Summary,0,,,,,,17/02/2021 22:02 CET,,Syngenta Crop Protection AG (Switzerland),,Yes,Draft,,,,,SmartDoc - EAME,,,,0,IT-615930,VV-729845,,N/A,PP321_12086,,,Syngenta,,,,,,,,,,S Lloyd,No,0,0,,,,,,,,,,,,,,,,0,,,1.0|CURRENT,090100b881afde22,,Syngenta Crop Protection AG (Switzerland),,Syngenta Crop Protection AG,,Original,,,,Internal Use Only,,,,,090100b881afde22,,,0,,,,,Sonia Ellis,,,Syngenta Crop Protection AG (Switzerland),,,European Union,General Lifecycle,,,40037_848583,,,,,,Ellis Sonia (ext) GBGU,,,,,Lambda-cyhalothrin - EU AIR5 - MCA Section 5 - Toxicology,,893,Summary / Assessment,,,,,,No,,090100b881afde22 (cv),,CP Vault Support,0,,No,Syngenta,24/02/2021 15:20 CET,,,Vault Migration,"EAME Reg Ops, Syngenta Business Administrator, PS Ops, NA Reg Ops, Global Reg Ops, Berangere Sochard, Simon Baker, Lily Williams, Clive Boxwell, Paul Parsons, Richard MacKenzie, Dan Pickford, Laurence Hand, Claire McCombie, Elaine Buss",Vault Migration,,application/vnd.openxmlformats-officedocument.wordprocessingml.document,Toxicology,3,,PP321,,,,0,No,,PI0015818,,No,40037_848583_2033983,No,01/09/2020,,Tox,No,0,,,,1,,,Syngenta Crop Protection AG,,17/02/2021 21:35 CET,,04/09/2020 14:34 CEST,,,,,,,,,VV-729845,Lloyd Sara,,5089990,,No,,,3b13f57d1244e77844eba73ce0ae9813,29/11/2019 08:47 CET,0,,,,,,Lambda-cyhalothrin - EU AIR5 - MCA Section 5 - Toxicology.pdf,
|
|
||||||
,,Country Specific,,,,,No,,,Yes,,,,,Syngenta,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 5 Reference List (1).doc,,,,,,,,,,,,,,,English,,,PP321,,Summary / Overview,,TRUE,"All Internal Users, Syngenta Read Only Restricted, Syngenta Read Only Non Restricted",,,,16/07/2019,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 5 Reference List,PI0015818,,,,0,,,OECD Tier 1 Summary,0,,,,,,12/09/2020 15:48 CEST,,Syngenta Crop Protection AG (Switzerland),,Yes,Final,,,,,SmartDoc - EAME,,,,0,IT-615436,VV-729844,,N/A,PP321_12084,,,,,,,,,,,,,S Ellis,No,0,0,,,,,,,,,,,,,,,,0,,,1.0|CURRENT,090100b881afde20,,Syngenta Crop Protection AG (Switzerland),,Syngenta Crop Protection AG,,Original,,,,Internal Use Only,,,,,090100b881afde20,,,0,,,,,Sonia Ellis,,,Syngenta Crop Protection AG (Switzerland),,,European Union,General Lifecycle,,,40037_848582,,,,,,,,,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 5 Reference List,,147,Summary / Assessment,,,,,,No,,090100b881afde20 (cv),,CP Vault Support,0,,No,,24/02/2021 15:20 CET,,,Vault Migration,"NA Reg Ops, Global Reg Ops, EAME Reg Ops, PS Ops, Syngenta Business Administrator, Berangere Sochard, Paul Parsons",Vault Migration,,application/msword,Toxicology,2,,PP321,,,,0,No,,PI0015818,,No,40037_848582_2001744,No,01/09/2020,,Tox,No,0,,,,0,,,Syngenta Crop Protection AG,,,,,,,,,,,,,,Ellis Sonia,,651264,,No,,,eb0fdf698b528b67f54635395ec7fb52,29/11/2019 08:47 CET,0,,,,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 5 Reference List.pdf,
|
|
||||||
,,Country Specific,,,,,No,,,Yes,,,,,Syngenta,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 4 Reference List (5).doc,,,,,,,,,,,,,,,English,,,PP321,,Summary / Overview,,TRUE,"All Internal Users, Syngenta Read Only Restricted, Syngenta Read Only Non Restricted",,,,16/07/2019,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 4 Reference List,PI0015818,,,,0,,,OECD Tier 1 Summary,0,,,,,,21/09/2020 15:21 CEST,,Syngenta Crop Protection AG (Switzerland),,Yes,Final,,,,,SmartDoc - EAME,,,,0,IT-618210,VV-729843,,N/A,PP321_12083,,,,,,,,,,,,,S Ellis,No,0,0,,,,,,,,,,,,,,,,0,,,1.0|CURRENT,090100b881afde1f,,Syngenta Crop Protection AG (Switzerland),,Syngenta Crop Protection AG,,Original,,,,Internal Use Only,,,,,090100b881afde1f,,,0,,,,,Sonia Ellis,,,Syngenta Crop Protection AG (Switzerland),,,European Union,General Lifecycle,,,40037_848581,,,,,,,,,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 4 Reference List,,151,Summary / Assessment,,,,,,No,,090100b881afde1f (cv),,CP Vault Support,0,,No,,24/02/2021 15:20 CET,,,Vault Migration,"EAME Reg Ops, Global Reg Ops, PS Ops, Syngenta Business Administrator, NA Reg Ops, Berangere Sochard, Melanie Bottoms, Dan Pickford, Lynda Farrelly",Vault Migration,,application/msword,Chemistry - Regulatory,2,,PP321,,,,0,No,,PI0015818,,No,40037_848581_2003177,No,01/09/2020,,Chem - Analytical Methods,No,0,,,,0,,,Syngenta Crop Protection AG,,,,,,,,,,,,,,Ellis Sonia,,699392,,No,,,2c476f477f6e2b55ecb537683f5d1cee,29/11/2019 08:47 CET,0,,,,,,Lambda-cyhalothrin - EU AIR5 - LCA Section 4 Reference List.pdf,
|
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
.idea/
|
.idea/
|
||||||
dist/
|
dist/
|
||||||
|
.angular
|
||||||
|
.husky
|
||||||
|
.editorconfig
|
||||||
|
.dockerignore
|
||||||
|
.eslintignore
|
||||||
|
.eslintrc.json
|
||||||
|
.gitignore
|
||||||
|
.prettierignore
|
||||||
|
.prettierrc
|
||||||
|
renovate.json
|
||||||
|
Running
|
||||||
|
|||||||
@ -5,3 +5,6 @@ dist
|
|||||||
coverage
|
coverage
|
||||||
node_modules
|
node_modules
|
||||||
bamboo-specs
|
bamboo-specs
|
||||||
|
docker
|
||||||
|
paligo-styles
|
||||||
|
paligo-theme
|
||||||
|
|||||||
@ -1,46 +1,10 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": ["**/*"],
|
"ignorePatterns": ["**/*"],
|
||||||
"plugins": ["@nx"],
|
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
|
||||||
"files": ["*.ts"],
|
|
||||||
"rules": {
|
|
||||||
"@nx/enforce-module-boundaries": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"enforceBuildableLibDependency": true,
|
|
||||||
"allow": [
|
|
||||||
"@services/**",
|
|
||||||
"@components/**",
|
|
||||||
"@guards/**",
|
|
||||||
"@users/**",
|
|
||||||
"@i18n/**",
|
|
||||||
"@utils/**",
|
|
||||||
"@models/**",
|
|
||||||
"@environments/**",
|
|
||||||
"@shared/**",
|
|
||||||
"@upload-download/**",
|
|
||||||
"@translations/**"
|
|
||||||
],
|
|
||||||
"depConstraints": [
|
|
||||||
{
|
|
||||||
"sourceTag": "*",
|
|
||||||
"onlyDependOnLibsWithTags": ["*"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.ts"],
|
|
||||||
"extends": ["plugin:@nx/typescript"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files": ["*.ts"],
|
"files": ["*.ts"],
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:@nx/angular",
|
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@angular-eslint/recommended",
|
"plugin:@angular-eslint/recommended",
|
||||||
"plugin:@angular-eslint/template/process-inline-templates",
|
"plugin:@angular-eslint/template/process-inline-templates",
|
||||||
@ -52,6 +16,7 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"rxjs/no-ignored-subscription": "warn",
|
"rxjs/no-ignored-subscription": "warn",
|
||||||
|
"@angular-eslint/prefer-standalone": "off",
|
||||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||||
"@angular-eslint/no-host-metadata-property": "error",
|
"@angular-eslint/no-host-metadata-property": "error",
|
||||||
"@angular-eslint/no-input-rename": "error",
|
"@angular-eslint/no-input-rename": "error",
|
||||||
@ -73,6 +38,7 @@
|
|||||||
"@typescript-eslint/indent": "off",
|
"@typescript-eslint/indent": "off",
|
||||||
"@typescript-eslint/lines-between-class-members": "off",
|
"@typescript-eslint/lines-between-class-members": "off",
|
||||||
"@typescript-eslint/ban-types": "off",
|
"@typescript-eslint/ban-types": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-argument": "off",
|
||||||
"@typescript-eslint/explicit-member-accessibility": [
|
"@typescript-eslint/explicit-member-accessibility": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
@ -283,7 +249,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.html"],
|
"files": ["*.html"],
|
||||||
"extends": ["plugin:@nx/angular-template", "plugin:@angular-eslint/template/recommended"]
|
"extends": ["plugin:@angular-eslint/template/recommended"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.html"],
|
"files": ["*.html"],
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -18,6 +18,7 @@
|
|||||||
*.launch
|
*.launch
|
||||||
.settings/
|
.settings/
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
redaction.iml
|
||||||
|
|
||||||
# IDE - VSCode
|
# IDE - VSCode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
@ -46,7 +47,4 @@ paligo-styles/style.css*
|
|||||||
|
|
||||||
migrations.json
|
migrations.json
|
||||||
*.iml
|
*.iml
|
||||||
|
/.nx/
|
||||||
docker-compose.yml
|
|
||||||
|
|
||||||
.nx/cache/
|
|
||||||
|
|||||||
@ -4,14 +4,32 @@ variables:
|
|||||||
PROJECT: red-ui
|
PROJECT: red-ui
|
||||||
DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile'
|
DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile'
|
||||||
|
|
||||||
workflow:
|
|
||||||
rules:
|
|
||||||
- when: always
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- project: 'gitlab/gitlab'
|
- project: 'gitlab/gitlab'
|
||||||
ref: 'main'
|
ref: 'main'
|
||||||
file: 'ci-templates/docker_build_nexus_v2.yml'
|
file: 'ci-templates/docker_build_nexus_v2.yml'
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE != "schedule"
|
||||||
|
|
||||||
|
sonarqube:
|
||||||
|
stage: test
|
||||||
|
image:
|
||||||
|
name: sonarsource/sonar-scanner-cli:11.1
|
||||||
|
entrypoint:
|
||||||
|
- ''
|
||||||
|
variables:
|
||||||
|
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
|
||||||
|
GIT_DEPTH: '0'
|
||||||
|
cache:
|
||||||
|
key: "${CI_JOB_NAME}"
|
||||||
|
paths:
|
||||||
|
- ".sonar/cache"
|
||||||
|
script:
|
||||||
|
- sonar-scanner
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||||
|
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
||||||
|
- if: "$CI_COMMIT_BRANCH =~ /^release/"
|
||||||
|
|
||||||
localazy update:
|
localazy update:
|
||||||
image: node:20.5
|
image: node:20.5
|
||||||
@ -22,21 +40,24 @@ localazy update:
|
|||||||
paths:
|
paths:
|
||||||
- .yarn-cache/
|
- .yarn-cache/
|
||||||
script:
|
script:
|
||||||
- git config user.email "${CI_EMAIL}"
|
# - git config user.email "${CI_EMAIL}"
|
||||||
- git config user.name "${CI_USERNAME}"
|
# - git config user.name "${CI_USERNAME}"
|
||||||
- git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
# - git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
||||||
|
- git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
||||||
- cd tools/localazy
|
- cd tools/localazy
|
||||||
- yarn install --cache-folder .yarn-cache
|
- yarn install --cache-folder .yarn-cache
|
||||||
- yarn start
|
- yarn start
|
||||||
- cd ../..
|
- cd ../..
|
||||||
- git add .
|
- git add .
|
||||||
- |-
|
- |-
|
||||||
CHANGES=$(git status --porcelain | wc -l)
|
CHANGES=$(git status --porcelain | wc -l)
|
||||||
if [ "$CHANGES" -gt "0" ]
|
if [ "$CHANGES" -gt "0" ]
|
||||||
then
|
then
|
||||||
git status
|
git status
|
||||||
git commit -m "push back localazy update"
|
git commit -m "push back localazy update"
|
||||||
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME} -o ci.skip
|
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME}
|
||||||
fi
|
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
||||||
only:
|
# git push
|
||||||
- scheduled
|
fi
|
||||||
|
rules:
|
||||||
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||||
|
|||||||
11
README.md
11
README.md
@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
### To Create a new Stack in rancher check [this Wiki page](https://wiki.iqser.com/pages/viewpage.action?spaceKey=RED&title=Work+with+kubectl)
|
### To Create a new Stack in rancher check [this Wiki page](https://wiki.iqser.com/pages/viewpage.action?spaceKey=RED&title=Work+with+kubectl)
|
||||||
|
|
||||||
## Code style
|
# Dependencies update guide
|
||||||
|
* When updating @pdftron/webviewer, make sure to change the version also in the angular.json and everywhere where the path to /assets/wv-recources is used
|
||||||
|
* Make sure the keycloak.js version is the same with the keycloak version from helm chart
|
||||||
|
|
||||||
* Always use `trackBy` in `*ngFor` loops (see shorthand below)
|
## Code style
|
||||||
```typescript
|
* Don't use `setInterval` without calling `clearInterval` in `ngOnDestroy` or in `destroyRef.onDestroy(() => clearInterval(intervalId))`
|
||||||
readonly trackBy = trackByFactory();
|
|
||||||
```
|
|
||||||
* Don't use `setInterval` without calling `clearInterval` in `ngOnDestroy`
|
|
||||||
* Never call getters in HTML templates
|
* Never call getters in HTML templates
|
||||||
|
|
||||||
## Keycloak Staging Config
|
## Keycloak Staging Config
|
||||||
|
|||||||
133
angular.json
Normal file
133
angular.json
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"cli": {
|
||||||
|
"packageManager": "yarn",
|
||||||
|
"schematicCollections": ["@angular-eslint/schematics"],
|
||||||
|
"analytics": "2bccdff1-3aff-4f10-b233-211065aa25d9"
|
||||||
|
},
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"red-ui": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss",
|
||||||
|
"skipTests": true
|
||||||
|
},
|
||||||
|
"@schematics/angular:class": {
|
||||||
|
"skipTests": true
|
||||||
|
},
|
||||||
|
"@schematics/angular:directive": {
|
||||||
|
"skipTests": true
|
||||||
|
},
|
||||||
|
"@schematics/angular:guard": {
|
||||||
|
"skipTests": true
|
||||||
|
},
|
||||||
|
"@schematics/angular:pipe": {
|
||||||
|
"skipTests": true
|
||||||
|
},
|
||||||
|
"@schematics/angular:service": {
|
||||||
|
"skipTests": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "apps/red-ui/src",
|
||||||
|
"prefix": "redaction",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular/build:application",
|
||||||
|
"options": {
|
||||||
|
"outputPath": {
|
||||||
|
"base": "dist/apps/red-ui"
|
||||||
|
},
|
||||||
|
"index": "apps/red-ui/src/index.html",
|
||||||
|
"polyfills": ["apps/red-ui/src/polyfills.ts"],
|
||||||
|
"tsConfig": "tsconfig.json",
|
||||||
|
"baseHref": "/ui/",
|
||||||
|
"assets": [
|
||||||
|
"apps/red-ui/src/favicon.ico",
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/@pdftron/webviewer/public/",
|
||||||
|
"output": "/assets/wv-resources/11.1.0/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "apps/red-ui/src/assets/",
|
||||||
|
"output": "/assets/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "libs/common-ui/src/assets/",
|
||||||
|
"output": "/assets/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/monaco-editor",
|
||||||
|
"output": "/assets/monaco-editor/"
|
||||||
|
},
|
||||||
|
"apps/red-ui/src/manifest.webmanifest"
|
||||||
|
],
|
||||||
|
"styles": ["apps/red-ui/src/styles.scss", "libs/common-ui/src/assets/styles/common-styles.scss"],
|
||||||
|
"stylePreprocessorOptions": {
|
||||||
|
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
|
||||||
|
},
|
||||||
|
"scripts": ["node_modules/chart.js/auto/auto.cjs"],
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false,
|
||||||
|
"namedChunks": true,
|
||||||
|
"browser": "apps/red-ui/src/main.ts"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "apps/red-ui/src/environments/environment.ts",
|
||||||
|
"with": "apps/red-ui/src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": {
|
||||||
|
"scripts": true,
|
||||||
|
"styles": {
|
||||||
|
"minify": true,
|
||||||
|
"inlineCritical": false
|
||||||
|
},
|
||||||
|
"fonts": true
|
||||||
|
},
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"namedChunks": false,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "6kb",
|
||||||
|
"maximumError": "20kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"serviceWorker": "ngsw-config.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular/build:dev-server",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "red-ui:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "red-ui:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["**/*.ts"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"extends": ["plugin:@typescript-eslint/recommended"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unsafe-return": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-argument": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-call": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/unbound-method": "off",
|
|
||||||
"@typescript-eslint/no-misused-promises": "off",
|
|
||||||
"@typescript-eslint/no-floating-promises": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import type { Config } from 'jest';
|
|
||||||
import { defaults } from 'jest-config';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
...defaults,
|
|
||||||
displayName: 'red-ui',
|
|
||||||
preset: '../../jest.preset.js',
|
|
||||||
setupFilesAfterEnv: ['jest-preset-angular/setup-jest.js', 'jest-extended/all'],
|
|
||||||
coverageDirectory: '../../coverage/apps/angular-jest',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.(ts|mjs|js|html)$': [
|
|
||||||
'jest-preset-angular',
|
|
||||||
{
|
|
||||||
tsconfig: '../../tsconfig.spec.json',
|
|
||||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
testEnvironment: 'jest-environment-jsdom',
|
|
||||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
|
||||||
snapshotSerializers: [
|
|
||||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
|
||||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
|
||||||
'jest-preset-angular/build/serializers/html-comment',
|
|
||||||
],
|
|
||||||
} as Config;
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "red-ui",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"projectType": "application",
|
|
||||||
"generators": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss",
|
|
||||||
"skipTests": true
|
|
||||||
},
|
|
||||||
"@schematics/angular:class": {
|
|
||||||
"skipTests": true
|
|
||||||
},
|
|
||||||
"@schematics/angular:directive": {
|
|
||||||
"skipTests": true
|
|
||||||
},
|
|
||||||
"@schematics/angular:guard": {
|
|
||||||
"skipTests": true
|
|
||||||
},
|
|
||||||
"@schematics/angular:pipe": {
|
|
||||||
"skipTests": true
|
|
||||||
},
|
|
||||||
"@schematics/angular:service": {
|
|
||||||
"skipTests": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sourceRoot": "apps/red-ui/src",
|
|
||||||
"prefix": "redaction",
|
|
||||||
"targets": {
|
|
||||||
"build": {
|
|
||||||
"executor": "@angular-devkit/build-angular:browser",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/apps/red-ui",
|
|
||||||
"index": "apps/red-ui/src/index.html",
|
|
||||||
"main": "apps/red-ui/src/main.ts",
|
|
||||||
"polyfills": "apps/red-ui/src/polyfills.ts",
|
|
||||||
"tsConfig": "apps/red-ui/tsconfig.json",
|
|
||||||
"baseHref": "/ui/",
|
|
||||||
"assets": [
|
|
||||||
"apps/red-ui/src/favicon.ico",
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "node_modules/@pdftron/webviewer/public/",
|
|
||||||
"output": "/assets/wv-resources/10.9.0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "apps/red-ui/src/assets/",
|
|
||||||
"output": "/assets/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "libs/common-ui/src/assets/",
|
|
||||||
"output": "/assets/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "node_modules/monaco-editor",
|
|
||||||
"output": "/assets/monaco-editor/"
|
|
||||||
},
|
|
||||||
"apps/red-ui/src/manifest.webmanifest"
|
|
||||||
],
|
|
||||||
"styles": ["apps/red-ui/src/styles.scss", "libs/common-ui/src/assets/styles/common-styles.scss"],
|
|
||||||
"stylePreprocessorOptions": {
|
|
||||||
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
|
|
||||||
},
|
|
||||||
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js", "node_modules/chart.js/dist/chart.js"],
|
|
||||||
"vendorChunk": true,
|
|
||||||
"extractLicenses": false,
|
|
||||||
"buildOptimizer": false,
|
|
||||||
"sourceMap": false,
|
|
||||||
"optimization": false,
|
|
||||||
"namedChunks": true
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "apps/red-ui/src/environments/environment.ts",
|
|
||||||
"with": "apps/red-ui/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optimization": {
|
|
||||||
"scripts": true,
|
|
||||||
"styles": {
|
|
||||||
"minify": true,
|
|
||||||
"inlineCritical": false
|
|
||||||
},
|
|
||||||
"fonts": true
|
|
||||||
},
|
|
||||||
"outputHashing": "all",
|
|
||||||
"sourceMap": false,
|
|
||||||
"namedChunks": false,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"vendorChunk": false,
|
|
||||||
"buildOptimizer": true,
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "2mb",
|
|
||||||
"maximumError": "5mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "6kb",
|
|
||||||
"maximumError": "20kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"serviceWorker": true,
|
|
||||||
"ngswConfigPath": "apps/red-ui/ngsw-config.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": ["{options.outputPath}"]
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"executor": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "red-ui:build"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"browserTarget": "red-ui:build:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "red-ui:build"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
||||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
import { ifNotLoggedIn } from '@common-ui/tenants/guards/if-not-logged-in.guard';
|
||||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||||
import { DashboardGuard } from '@guards/dashboard-guard.service';
|
import { DashboardGuard } from '@guards/dashboard-guard.service';
|
||||||
@ -8,21 +8,21 @@ import { DossierFilesGuard } from '@guards/dossier-files-guard';
|
|||||||
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
||||||
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
|
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
|
||||||
import { editAttributeGuard } from '@guards/edit-attribute.guard';
|
import { isNotEditingFileAttributeGuard } from '@guards/file-attribute.guard';
|
||||||
import { FeaturesGuard } from '@guards/features-guard.service';
|
import { FeaturesGuard } from '@guards/features-guard.service';
|
||||||
import { ifLoggedIn } from '@guards/if-logged-in.guard';
|
import { ifLoggedIn } from '@guards/if-logged-in.guard';
|
||||||
import { ifNotLoggedIn } from '@guards/if-not-logged-in.guard';
|
|
||||||
import { TrashGuard } from '@guards/trash.guard';
|
import { TrashGuard } from '@guards/trash.guard';
|
||||||
import { CompositeRouteGuard, DEFAULT_REDIRECT_KEY, IqserPermissionsGuard, IqserRoutes, orderedAsyncGuards } from '@iqser/common-ui';
|
import { CompositeRouteGuard, DEFAULT_REDIRECT_KEY, IqserPermissionsGuard, IqserRoutes, orderedAsyncGuards } from '@iqser/common-ui';
|
||||||
import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
|
import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
|
||||||
import { doesNotHaveAnyRole, hasAnyRole, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
import { doesNotHaveAnyRole, hasAnyRole, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
import { CustomRouteReuseStrategy } from '@iqser/common-ui/lib/utils';
|
import { CustomRouteReuseStrategy } from '@iqser/common-ui/lib/utils';
|
||||||
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { mainGuard } from '@utils/main.guard';
|
import { mainGuard } from '@utils/main.guard';
|
||||||
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
||||||
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
|
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
|
||||||
|
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||||
|
|
||||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
{
|
{
|
||||||
path: `:${DOSSIER_ID}`,
|
path: `:${DOSSIER_ID}`,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
canDeactivate: [editAttributeGuard],
|
canDeactivate: [isNotEditingFileAttributeGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [DossierFilesGuard],
|
routeGuards: [DossierFilesGuard],
|
||||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
|
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
|
||||||
@ -49,7 +49,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
skeleton: 'dossier',
|
skeleton: 'dossier',
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./modules/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
|
loadChildren: () => import('./modules/dossier-overview/dossier-overview.routes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
||||||
@ -63,12 +63,12 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
redirectTo: '/auth-error',
|
redirectTo: '/auth-error',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./modules/file-preview/file-preview.module').then(m => m.FilePreviewModule),
|
loadChildren: () => import('./modules/file-preview/file-preview.routes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
loadChildren: () => import('./modules/dossiers-listing/dossiers-listing.module').then(m => m.DossiersListingModule),
|
loadChildren: () => import('./modules/dossiers-listing/dossiers-listing.routes'),
|
||||||
data: {
|
data: {
|
||||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate],
|
breadcrumbs: [BreadcrumbTypes.dossierTemplate],
|
||||||
},
|
},
|
||||||
@ -77,11 +77,10 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${ARCHIVE_ROUTE}`,
|
path: `${ARCHIVE_ROUTE}`,
|
||||||
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
|
loadChildren: () => import('./modules/archive/archive.routes'),
|
||||||
canActivate: [CompositeRouteGuard, webViewerLoadedGuard(), loadArchivedDossiersGuard()],
|
canActivate: [CompositeRouteGuard, loadArchivedDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [FeaturesGuard],
|
routeGuards: [FeaturesGuard],
|
||||||
features: [DOSSIERS_ARCHIVE],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,16 +98,16 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'account',
|
path: 'account',
|
||||||
loadChildren: () => import('./modules/account/account.module').then(m => m.AccountModule),
|
loadChildren: () => import('./modules/account/account.routes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: 'admin',
|
||||||
loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule),
|
loadChildren: () => import('./modules/admin/admin.routes'),
|
||||||
canActivate: [RedRoleGuard],
|
canActivate: [RedRoleGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'dashboard',
|
path: 'dashboard',
|
||||||
loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule),
|
loadChildren: () => import('./modules/dashboard/dashboard.routes'),
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
|
||||||
@ -147,7 +146,7 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
|
loadComponent: () => import('./modules/search/search-screen/search-screen.component'),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
@ -159,7 +158,7 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'trash',
|
path: 'trash',
|
||||||
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
|
loadChildren: () => import('./modules/trash/trash.routes'),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
|
||||||
@ -205,18 +204,13 @@ const routes: IqserRoutes = [
|
|||||||
component: TenantSelectComponent,
|
component: TenantSelectComponent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':tenant',
|
path: 'main',
|
||||||
redirectTo: ':tenant/main',
|
|
||||||
pathMatch: 'full',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: ':tenant/main',
|
|
||||||
canActivate: [orderedAsyncGuards([ifLoggedIn(), hasAnyRole(), mainGuard()])],
|
canActivate: [orderedAsyncGuards([ifLoggedIn(), hasAnyRole(), mainGuard()])],
|
||||||
component: BaseScreenComponent,
|
component: BaseScreenComponent,
|
||||||
children: mainRoutes,
|
children: mainRoutes,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':tenant/auth-error',
|
path: 'auth-error',
|
||||||
component: AuthErrorComponent,
|
component: AuthErrorComponent,
|
||||||
canActivate: [doesNotHaveAnyRole()],
|
canActivate: [doesNotHaveAnyRole()],
|
||||||
},
|
},
|
||||||
@ -228,7 +222,13 @@ const routes: IqserRoutes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled', bindToComponentInputs: true })],
|
imports: [
|
||||||
|
RouterModule.forRoot(routes, {
|
||||||
|
scrollPositionRestoration: 'enabled',
|
||||||
|
bindToComponentInputs: true,
|
||||||
|
paramsInheritanceStrategy: 'always',
|
||||||
|
}),
|
||||||
|
],
|
||||||
providers: [{ provide: RouteReuseStrategy, useExisting: CustomRouteReuseStrategy }],
|
providers: [{ provide: RouteReuseStrategy, useExisting: CustomRouteReuseStrategy }],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,26 +4,27 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
|||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { AppConfig } from '@red/domain';
|
import { AppConfig } from '@red/domain';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
import { filter, map, switchMap, take } from 'rxjs/operators';
|
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
|
import { APP_TYPE_PATHS } from '@common-ui/utils/constants';
|
||||||
|
import { MatIconRegistry } from '@angular/material/icon';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
import { TenantsService } from '@common-ui/tenants';
|
||||||
|
|
||||||
function loadCustomTheme() {
|
export function loadCustomTheme(cssFileName: string) {
|
||||||
const cssFileName = getConfig<AppConfig>().THEME;
|
const head = document.getElementsByTagName('head')[0];
|
||||||
|
const link = document.createElement('link');
|
||||||
if (cssFileName) {
|
link.id = cssFileName;
|
||||||
const head = document.getElementsByTagName('head')[0];
|
link.rel = 'stylesheet';
|
||||||
const link = document.createElement('link');
|
link.type = 'text/css';
|
||||||
link.id = cssFileName;
|
link.href = 'assets/styles/themes/' + cssFileName + '.css';
|
||||||
link.rel = 'stylesheet';
|
link.media = 'all';
|
||||||
link.type = 'text/css';
|
head.appendChild(link);
|
||||||
link.href = 'assets/styles/themes/' + cssFileName + '.css';
|
|
||||||
link.media = 'all';
|
|
||||||
head.appendChild(link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-root',
|
selector: 'redaction-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor(
|
constructor(
|
||||||
@ -34,9 +35,12 @@ export class AppComponent {
|
|||||||
userPreferenceService: UserPreferenceService,
|
userPreferenceService: UserPreferenceService,
|
||||||
renderer: Renderer2,
|
renderer: Renderer2,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
|
private readonly _iconRegistry: MatIconRegistry,
|
||||||
|
private readonly _sanitizer: DomSanitizer,
|
||||||
|
private readonly _tenantsService: TenantsService,
|
||||||
) {
|
) {
|
||||||
|
const config = getConfig<AppConfig>();
|
||||||
renderer.addClass(document.body, userPreferenceService.getTheme());
|
renderer.addClass(document.body, userPreferenceService.getTheme());
|
||||||
loadCustomTheme();
|
|
||||||
|
|
||||||
const removeQueryParams = _router.events.pipe(
|
const removeQueryParams = _router.events.pipe(
|
||||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
@ -47,9 +51,25 @@ export class AppComponent {
|
|||||||
);
|
);
|
||||||
removeQueryParams.subscribe();
|
removeQueryParams.subscribe();
|
||||||
|
|
||||||
if (getConfig().IS_DOCUMINE) {
|
this._tenantsService
|
||||||
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
|
.waitForSettingTenant()
|
||||||
}
|
.pipe(
|
||||||
|
tap(() => {
|
||||||
|
const isDocumine = this._tenantsService.activeTenant.documine;
|
||||||
|
const logo = isDocumine ? 'documine' : 'redaction';
|
||||||
|
_iconRegistry.addSvgIconInNamespace(
|
||||||
|
'iqser',
|
||||||
|
'logo',
|
||||||
|
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}-logo.svg`),
|
||||||
|
);
|
||||||
|
if (isDocumine) {
|
||||||
|
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
|
||||||
|
}
|
||||||
|
loadCustomTheme(isDocumine ? APP_TYPE_PATHS.SCM : APP_TYPE_PATHS.REDACT);
|
||||||
|
}),
|
||||||
|
take(1),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeKeycloakQueryParams() {
|
#removeKeycloakQueryParams() {
|
||||||
|
|||||||
@ -1,11 +1,20 @@
|
|||||||
import { DatePipe as BaseDatePipe } from '@angular/common';
|
import { APP_BASE_HREF, DatePipe as BaseDatePipe } from '@angular/common';
|
||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
import { ENVIRONMENT_INITIALIZER, ErrorHandler, inject, NgModule } from '@angular/core';
|
import { ErrorHandler, inject, NgModule, provideEnvironmentInitializer } from '@angular/core';
|
||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
import { MAT_TOOLTIP_DEFAULT_OPTIONS } from '@angular/material/tooltip';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { MatMenu, MatMenuContent, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
|
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||||
|
import { MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltip } from '@angular/material/tooltip';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
|
import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button';
|
||||||
|
import { EmptyStateComponent } from '@common-ui/empty-state';
|
||||||
|
import { HelpModeKey } from '@common-ui/help-mode/types';
|
||||||
|
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
||||||
|
import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component';
|
||||||
|
import { GET_TENANT_FROM_PATH_FN, UI_ROOT } from '@common-ui/utils';
|
||||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||||
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
|
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
|
||||||
@ -21,51 +30,49 @@ import { UserMenuComponent } from '@components/user-menu/user-menu.component';
|
|||||||
import { environment } from '@environments/environment';
|
import { environment } from '@environments/environment';
|
||||||
import {
|
import {
|
||||||
CachingModule,
|
CachingModule,
|
||||||
ChevronButtonComponent,
|
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
EmptyStateComponent,
|
HelpButtonComponent,
|
||||||
HelpModeKey,
|
HelpModeComponent,
|
||||||
HiddenActionDirective,
|
HiddenActionDirective,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
InputWithActionComponent,
|
|
||||||
IqserAllowDirective,
|
IqserAllowDirective,
|
||||||
IqserDenyDirective,
|
IqserDenyDirective,
|
||||||
IqserHelpModeModule,
|
|
||||||
IqserListingModule,
|
IqserListingModule,
|
||||||
IqserLoadingModule,
|
IqserLoadingModule,
|
||||||
IqserTranslateModule,
|
IqserTranslateModule,
|
||||||
LanguageService,
|
LanguageService,
|
||||||
MAX_RETRIES_ON_SERVER_ERROR,
|
MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
RoundCheckboxComponent,
|
|
||||||
SERVER_ERROR_SKIP_PATHS,
|
SERVER_ERROR_SKIP_PATHS,
|
||||||
ServerErrorInterceptor,
|
ServerErrorInterceptor,
|
||||||
StopPropagationDirective,
|
StopPropagationDirective,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { CommonUiModule } from '@iqser/common-ui/lib/common-ui.module';
|
import { CommonUiModule } from '@iqser/common-ui/lib/common-ui.module';
|
||||||
|
import { provideHelpMode } from '@iqser/common-ui/lib/help-mode/utils/help-mode.provider';
|
||||||
import { LogoComponent, SkeletonComponent, ToastComponent } from '@iqser/common-ui/lib/shared';
|
import { LogoComponent, SkeletonComponent, ToastComponent } from '@iqser/common-ui/lib/shared';
|
||||||
import { TenantPipe, TenantsModule } from '@iqser/common-ui/lib/tenants';
|
import { TenantsModule } from '@iqser/common-ui/lib/tenants';
|
||||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
import { InitialsAvatarComponent, IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
||||||
import { MissingTranslationHandler } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { AppConfig, ILoggerConfig } from '@red/domain';
|
import { AppConfig, ILoggerConfig } from '@red/domain';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
||||||
import { GlobalErrorHandler } from '@services/global-error-handler.service';
|
import { GlobalErrorHandler } from '@services/global-error-handler.service';
|
||||||
import { LoggerRulesService } from '@services/logger-rules.service';
|
import { LoggerRulesService } from '@services/logger-rules.service';
|
||||||
|
import { provideCustomDateFormatter } from '@shared/custom-date-formatting.provider';
|
||||||
|
import { NavigateLastDossiersScreenDirective } from '@shared/directives/navigate-last-dossiers-screen.directive';
|
||||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
|
||||||
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
|
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { UI_CACHES } from '@utils/constants';
|
import { UI_CACHES } from '@utils/constants';
|
||||||
import { REDMissingTranslationHandler } from '@utils/missing-translations-handler';
|
import { ColorPickerService } from 'ngx-color-picker';
|
||||||
import { LoggerModule, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_SERVICE } from 'ngx-logger';
|
import { LoggerModule, NGXLogger, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_SERVICE } from 'ngx-logger';
|
||||||
import { ToastrModule } from 'ngx-toastr';
|
import { ToastrModule } from 'ngx-toastr';
|
||||||
import * as helpModeKeys from '../assets/help-mode/help-mode-keys.json';
|
import helpModeKeys from '../assets/help-mode/help-mode-keys.json';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
import { IconsModule } from './modules/icons/icons.module';
|
||||||
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
||||||
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
|
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
|
||||||
|
|
||||||
@ -89,8 +96,6 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
SharedModule,
|
|
||||||
FileUploadDownloadModule,
|
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
MonacoEditorModule,
|
MonacoEditorModule,
|
||||||
CommonUiModule.forRoot({
|
CommonUiModule.forRoot({
|
||||||
@ -103,7 +108,6 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
existingRoleGuard: RedRoleGuard,
|
existingRoleGuard: RedRoleGuard,
|
||||||
}),
|
}),
|
||||||
CachingModule.forRoot(UI_CACHES),
|
CachingModule.forRoot(UI_CACHES),
|
||||||
IqserHelpModeModule.forRoot(helpModeKeys as HelpModeKey[]),
|
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
ToastrModule.forRoot({
|
ToastrModule.forRoot({
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
@ -113,7 +117,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
resetTimeoutOnDuplicate: true,
|
resetTimeoutOnDuplicate: true,
|
||||||
}),
|
}),
|
||||||
TenantsModule.forRoot(),
|
TenantsModule.forRoot(),
|
||||||
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY || '/assets/i18n/redact/' }),
|
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY }),
|
||||||
IqserLoadingModule.forRoot(),
|
IqserLoadingModule.forRoot(),
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||||
LoggerModule.forRoot(undefined, {
|
LoggerModule.forRoot(undefined, {
|
||||||
@ -131,7 +135,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
features: {
|
features: {
|
||||||
ANNOTATIONS: {
|
ANNOTATIONS: {
|
||||||
color: 'aqua',
|
color: 'aqua',
|
||||||
enabled: true,
|
enabled: false,
|
||||||
level: NgxLoggerLevel.DEBUG,
|
level: NgxLoggerLevel.DEBUG,
|
||||||
},
|
},
|
||||||
FILTERS: {
|
FILTERS: {
|
||||||
@ -141,10 +145,10 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
ROUTES: {
|
ROUTES: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
PDF: {
|
PDF: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
FILE: {
|
FILE: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -156,7 +160,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
REDACTION_LOG: {
|
REDACTION_LOG: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
VIEWED_PAGES: {
|
VIEWED_PAGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -167,6 +171,9 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
DOSSIERS_CHANGES: {
|
DOSSIERS_CHANGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
GUARDS: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as ILoggerConfig,
|
} as ILoggerConfig,
|
||||||
},
|
},
|
||||||
@ -183,11 +190,40 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
IqserDenyDirective,
|
IqserDenyDirective,
|
||||||
IqserListingModule,
|
IqserListingModule,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
TenantPipe,
|
|
||||||
MatDividerModule,
|
MatDividerModule,
|
||||||
ChevronButtonComponent,
|
ChevronButtonComponent,
|
||||||
|
InitialsAvatarComponent,
|
||||||
|
HelpModeComponent,
|
||||||
|
HelpButtonComponent,
|
||||||
|
MatMenuTrigger,
|
||||||
|
MatMenuItem,
|
||||||
|
MatIcon,
|
||||||
|
MatMenu,
|
||||||
|
MatMenuContent,
|
||||||
|
MatTooltip,
|
||||||
|
MatProgressSpinner,
|
||||||
|
IconsModule,
|
||||||
|
NavigateLastDossiersScreenDirective,
|
||||||
|
DatePipe,
|
||||||
|
TranslateModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
{
|
||||||
|
provide: UI_ROOT,
|
||||||
|
useValue: '/ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: APP_BASE_HREF,
|
||||||
|
useFactory: () => {
|
||||||
|
const uiRoot = inject(UI_ROOT);
|
||||||
|
const tenant = inject(GET_TENANT_FROM_PATH_FN)();
|
||||||
|
console.log(tenant);
|
||||||
|
const appBaseHref = uiRoot + '/' + tenant;
|
||||||
|
|
||||||
|
inject(NGXLogger).info('Provide APP_BASE_HREF:', appBaseHref);
|
||||||
|
return appBaseHref;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
multi: true,
|
multi: true,
|
||||||
@ -197,18 +233,10 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
provide: ErrorHandler,
|
provide: ErrorHandler,
|
||||||
useClass: GlobalErrorHandler,
|
useClass: GlobalErrorHandler,
|
||||||
},
|
},
|
||||||
{
|
provideEnvironmentInitializer(async () => {
|
||||||
provide: ENVIRONMENT_INITIALIZER,
|
const languageService = inject(LanguageService);
|
||||||
multi: true,
|
return languageService.setInitialLanguage();
|
||||||
useValue: async () => {
|
}),
|
||||||
const languageService = inject(LanguageService);
|
|
||||||
return languageService.setInitialLanguage();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: MissingTranslationHandler,
|
|
||||||
useClass: REDMissingTranslationHandler,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: MAX_RETRIES_ON_SERVER_ERROR,
|
provide: MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR,
|
useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
@ -229,10 +257,14 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
|
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
|
||||||
useValue: {
|
useValue: {
|
||||||
disableTooltipInteractivity: true,
|
disableTooltipInteractivity: true,
|
||||||
|
showDelay: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BaseDatePipe,
|
BaseDatePipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
|
...provideCustomDateFormatter(),
|
||||||
|
...provideHelpMode(helpModeKeys as HelpModeKey[]),
|
||||||
|
ColorPickerService,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,17 +2,17 @@
|
|||||||
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
|
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="adminName && adminUrl"
|
*ngIf="adminName && adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-name-and-link' | translate : { adminName: adminName, adminUrl: adminUrl }"
|
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: adminName, adminUrl: adminUrl }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="adminName && !adminUrl"
|
*ngIf="adminName && !adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-name' | translate : { adminName: adminName }"
|
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: adminName }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="!adminName && adminUrl"
|
*ngIf="!adminName && adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-link' | translate : { adminName: adminName }"
|
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: adminName }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<a (click)="userService.logout()" translate="auth-error.logout"></a>
|
<a (click)="userService.logout()" translate="auth-error.logout"></a>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { AppConfig } from '@red/domain';
|
|||||||
selector: 'redaction-auth-error',
|
selector: 'redaction-auth-error',
|
||||||
templateUrl: './auth-error.component.html',
|
templateUrl: './auth-error.component.html',
|
||||||
styleUrls: ['./auth-error.component.scss'],
|
styleUrls: ['./auth-error.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class AuthErrorComponent {
|
export class AuthErrorComponent {
|
||||||
readonly #config = getConfig<AppConfig>();
|
readonly #config = getConfig<AppConfig>();
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
<redaction-breadcrumbs></redaction-breadcrumbs>
|
<redaction-breadcrumbs></redaction-breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a [matTooltip]="'top-bar.navigation-items.back-to-dashboard' | translate" [routerLink]="['/'] | tenant" class="logo">
|
<a [matTooltip]="'top-bar.navigation-items.back-to-dashboard' | translate" [routerLink]="['/main']" class="logo">
|
||||||
<div [attr.help-mode-key]="'home'" class="actions">
|
<div [attr.help-mode-key]="'home'" class="actions">
|
||||||
<iqser-logo (iqserHiddenAction)="userPreferenceService.toggleDevFeatures()" icon="iqser:logo"></iqser-logo>
|
<iqser-logo (iqserHiddenAction)="userPreferenceService.toggleDevFeatures()" icon="iqser:logo"></iqser-logo>
|
||||||
<div class="app-name">{{ titleService.getTitle() }}</div>
|
<div class="app-name">{{ titleService.getTitle() }}</div>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
[placeholder]="'search.placeholder' | translate"
|
[placeholder]="'search.placeholder' | translate"
|
||||||
></redaction-spotlight-search>
|
></redaction-spotlight-search>
|
||||||
|
|
||||||
<iqser-help-button [attr.help-mode-key]="'help_mode'" id="help-mode-button"></iqser-help-button>
|
<iqser-help-button [dialogButton]="false"></iqser-help-button>
|
||||||
|
|
||||||
<redaction-notifications
|
<redaction-notifications
|
||||||
*ngIf="currentUser.isUser || currentUser.isManager"
|
*ngIf="currentUser.isUser || currentUser.isManager"
|
||||||
|
|||||||
@ -8,13 +8,11 @@ import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-se
|
|||||||
import { filter, map, startWith } from 'rxjs/operators';
|
import { filter, map, startWith } from 'rxjs/operators';
|
||||||
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
||||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||||
import { FeaturesService } from '@services/features.service';
|
import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '@red/domain';
|
||||||
import { ARCHIVE_ROUTE, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE } from '@red/domain';
|
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
|
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { List, shareDistinctLast } from '@iqser/common-ui/lib/utils';
|
import { List, shareDistinctLast } from '@iqser/common-ui/lib/utils';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
|
||||||
|
|
||||||
const isNavigationStart = (event: unknown): event is NavigationStart => event instanceof NavigationStart;
|
const isNavigationStart = (event: unknown): event is NavigationStart => event instanceof NavigationStart;
|
||||||
const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
||||||
@ -22,14 +20,9 @@ const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-screen.component.html',
|
templateUrl: './base-screen.component.html',
|
||||||
styleUrls: ['./base-screen.component.scss'],
|
styleUrls: ['./base-screen.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class BaseScreenComponent {
|
export class BaseScreenComponent {
|
||||||
readonly #navigationStart$ = this._router.events.pipe(
|
|
||||||
filter(isNavigationStart),
|
|
||||||
map(event => event.url),
|
|
||||||
startWith(this._router.url),
|
|
||||||
shareDistinctLast(),
|
|
||||||
);
|
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
readonly documentViewer = inject(REDDocumentViewer);
|
readonly documentViewer = inject(REDDocumentViewer);
|
||||||
readonly currentUser = this.userService.currentUser;
|
readonly currentUser = this.userService.currentUser;
|
||||||
@ -43,7 +36,6 @@ export class BaseScreenComponent {
|
|||||||
{
|
{
|
||||||
text: this._translateService.instant('search.active-dossiers'),
|
text: this._translateService.instant('search.active-dossiers'),
|
||||||
icon: 'red:enter',
|
icon: 'red:enter',
|
||||||
hide: () => !this._featuresService.isEnabled(DOSSIERS_ARCHIVE),
|
|
||||||
action: (query): void => this.#search(query, [], true),
|
action: (query): void => this.#search(query, [], true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -52,15 +44,19 @@ export class BaseScreenComponent {
|
|||||||
action: (query): void => this.#search(query, []),
|
action: (query): void => this.#search(query, []),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
readonly isSearchScreen$ = this.#navigationStart$.pipe(map(isSearchScreen));
|
|
||||||
readonly config = getConfig();
|
readonly config = getConfig();
|
||||||
|
readonly #navigationStart$ = this._router.events.pipe(
|
||||||
|
filter(isNavigationStart),
|
||||||
|
map(event => event.url),
|
||||||
|
startWith(this._router.url),
|
||||||
|
shareDistinctLast(),
|
||||||
|
);
|
||||||
|
readonly isSearchScreen$ = this.#navigationStart$.pipe(map(isSearchScreen));
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
activatedRoute: ActivatedRoute,
|
activatedRoute: ActivatedRoute,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _featuresService: FeaturesService,
|
|
||||||
protected readonly _tenantsService: TenantsService,
|
|
||||||
readonly permissionsService: IqserPermissionsService,
|
readonly permissionsService: IqserPermissionsService,
|
||||||
readonly userService: UserService,
|
readonly userService: UserService,
|
||||||
readonly userPreferenceService: UserPreferenceService,
|
readonly userPreferenceService: UserPreferenceService,
|
||||||
@ -94,7 +90,7 @@ export class BaseScreenComponent {
|
|||||||
|
|
||||||
#search(query: string, dossierIds: string[], onlyActive = false) {
|
#search(query: string, dossierIds: string[], onlyActive = false) {
|
||||||
const queryParams = { query, dossierIds: dossierIds.join(','), onlyActive };
|
const queryParams = { query, dossierIds: dossierIds.join(','), onlyActive };
|
||||||
this._router.navigate([`/${this._tenantsService.activeTenantId}/main/search`], { queryParams }).then();
|
this._router.navigate(['/main/search'], { queryParams }).then();
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchThisDossier(query: string) {
|
#searchThisDossier(query: string) {
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
[id]="first ? 'navigateToActiveDossiers' : ''"
|
[id]="first ? 'navigateToActiveDossiers' : ''"
|
||||||
[matTooltip]="breadcrumb.options.clamp && (breadcrumb.name$ | async)"
|
[matTooltip]="breadcrumb.options.clamp && (breadcrumb.name$ | async)"
|
||||||
[routerLinkActiveOptions]="breadcrumb.options.routerLinkActiveOptions || { exact: false }"
|
[routerLinkActiveOptions]="breadcrumb.options.routerLinkActiveOptions || { exact: false }"
|
||||||
[routerLink]="breadcrumb.options.routerLink | tenant"
|
[routerLink]="breadcrumb.options.routerLink"
|
||||||
class="breadcrumb"
|
class="breadcrumb"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
>
|
>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<div id="breadcrumbs-menu-items">
|
<div id="breadcrumbs-menu-items">
|
||||||
<a
|
<a
|
||||||
*ngFor="let option of breadcrumb.options.options"
|
*ngFor="let option of breadcrumb.options.options"
|
||||||
[routerLink]="option.options.routerLink | tenant"
|
[routerLink]="option.options.routerLink"
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { Breadcrumb, BreadcrumbDisplayType, BreadcrumbsService } from '@services
|
|||||||
selector: 'redaction-breadcrumbs',
|
selector: 'redaction-breadcrumbs',
|
||||||
templateUrl: './breadcrumbs.component.html',
|
templateUrl: './breadcrumbs.component.html',
|
||||||
styleUrls: ['./breadcrumbs.component.scss'],
|
styleUrls: ['./breadcrumbs.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class BreadcrumbsComponent {
|
export class BreadcrumbsComponent {
|
||||||
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
|
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { firstValueFrom } from 'rxjs';
|
|||||||
entitiesService: FileDownloadService,
|
entitiesService: FileDownloadService,
|
||||||
component: DownloadsListScreenComponent,
|
component: DownloadsListScreenComponent,
|
||||||
}),
|
}),
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy {
|
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy {
|
||||||
readonly #interval: number;
|
readonly #interval: number;
|
||||||
|
|||||||
@ -32,19 +32,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
(click)="markRead([notification], true)"
|
(click)="handleMarkReadEvent($event, [notification], true)"
|
||||||
*ngFor="let notification of group.notifications; trackBy: trackBy"
|
*ngFor="let notification of group.notifications; trackBy: trackBy"
|
||||||
[class.unread]="!notification.readDate"
|
[class.unread]="!notification.readDate"
|
||||||
[id]="'notifications-mark-as-read-' + notification.id + '-btn'"
|
[id]="'notifications-mark-as-read-' + notification.id + '-btn'"
|
||||||
class="notification"
|
class="notification"
|
||||||
iqserStopPropagation
|
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
>
|
>
|
||||||
<iqser-initials-avatar [user]="notification.userId"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="notification.userId"></iqser-initials-avatar>
|
||||||
|
|
||||||
<div class="notification-content">
|
<div class="notification-content">
|
||||||
<div [innerHTML]="notification.message"></div>
|
<div [innerHTML]="notification.message"></div>
|
||||||
<div class="small-label mt-2">{{ notification.creationDate | date : 'exactDate' }}</div>
|
<div class="small-label mt-2">{{ notification.creationDate | date: 'exactDate' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -52,7 +51,7 @@
|
|||||||
[id]="'notifications-mark-' + notification.id"
|
[id]="'notifications-mark-' + notification.id"
|
||||||
class="dot"
|
class="dot"
|
||||||
iqserStopPropagation
|
iqserStopPropagation
|
||||||
matTooltip="{{ 'notifications.mark-as' | translate : { type: notification.readDate ? 'unread' : 'read' } }}"
|
matTooltip="{{ 'notifications.mark-as' | translate: { type: notification.readDate ? 'unread' : 'read' } }}"
|
||||||
matTooltipPosition="before"
|
matTooltipPosition="before"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mat-mdc-menu-item.notification {
|
.mat-mdc-menu-item.notification {
|
||||||
padding: 8px 26px 10px 8px;
|
padding: 8px 26px 10px 8px !important;
|
||||||
margin: 2px 0 0 0;
|
margin: 2px 0 0 0;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -25,6 +25,7 @@ function chronologically(first: string, second: string) {
|
|||||||
selector: 'redaction-notifications',
|
selector: 'redaction-notifications',
|
||||||
templateUrl: './notifications.component.html',
|
templateUrl: './notifications.component.html',
|
||||||
styleUrls: ['./notifications.component.scss'],
|
styleUrls: ['./notifications.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class NotificationsComponent {
|
export class NotificationsComponent {
|
||||||
readonly hasUnreadNotifications$: Observable<boolean>;
|
readonly hasUnreadNotifications$: Observable<boolean>;
|
||||||
@ -58,6 +59,15 @@ export class NotificationsComponent {
|
|||||||
await this._notificationsService.toggleNotificationRead(notificationsIds, isRead);
|
await this._notificationsService.toggleNotificationRead(notificationsIds, isRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleMarkReadEvent(event: MouseEvent, notifications: Notification[] = this._notificationsService.all, isRead = true) {
|
||||||
|
if (!(event.target as HTMLBaseElement).href) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.markRead(notifications, isRead);
|
||||||
|
}
|
||||||
|
|
||||||
#groupNotifications(notifications: Notification[]): NotificationsGroup[] {
|
#groupNotifications(notifications: Notification[]): NotificationsGroup[] {
|
||||||
const todayTranslation = this._translateService.instant('today');
|
const todayTranslation = this._translateService.instant('today');
|
||||||
const groupedMap = notifications.groupBy(n => {
|
const groupedMap = notifications.groupBy(n => {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
width: 900px;
|
width: 1000px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,5 +5,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
templateUrl: './dashboard-skeleton.component.html',
|
templateUrl: './dashboard-skeleton.component.html',
|
||||||
styleUrls: ['./dashboard-skeleton.component.scss'],
|
styleUrls: ['./dashboard-skeleton.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class DashboardSkeletonComponent {}
|
export class DashboardSkeletonComponent {}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } fr
|
|||||||
templateUrl: './dossier-skeleton.component.html',
|
templateUrl: './dossier-skeleton.component.html',
|
||||||
styleUrls: ['./dossier-skeleton.component.scss'],
|
styleUrls: ['./dossier-skeleton.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class DossierSkeletonComponent implements OnInit {
|
export class DossierSkeletonComponent implements OnInit {
|
||||||
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;
|
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;
|
||||||
|
|||||||
@ -5,5 +5,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
templateUrl: './skeleton-stats.component.html',
|
templateUrl: './skeleton-stats.component.html',
|
||||||
styleUrls: ['./skeleton-stats.component.scss'],
|
styleUrls: ['./skeleton-stats.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SkeletonStatsComponent {}
|
export class SkeletonStatsComponent {}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
templateUrl: './skeleton-top-bar.component.html',
|
templateUrl: './skeleton-top-bar.component.html',
|
||||||
styleUrls: ['./skeleton-top-bar.component.scss'],
|
styleUrls: ['./skeleton-top-bar.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SkeletonTopBarComponent {
|
export class SkeletonTopBarComponent {
|
||||||
constructor(readonly titleService: Title) {}
|
constructor(readonly titleService: Title) {}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { MatMenuTrigger } from '@angular/material/menu';
|
|||||||
templateUrl: './spotlight-search.component.html',
|
templateUrl: './spotlight-search.component.html',
|
||||||
styleUrls: ['./spotlight-search.component.scss'],
|
styleUrls: ['./spotlight-search.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class SpotlightSearchComponent {
|
export class SpotlightSearchComponent {
|
||||||
@Input() actions: readonly SpotlightSearchAction[];
|
@Input() actions: readonly SpotlightSearchAction[];
|
||||||
|
|||||||
@ -9,6 +9,7 @@ interface ITenant extends IStoredTenantId {
|
|||||||
selector: 'app-tenants-menu',
|
selector: 'app-tenants-menu',
|
||||||
templateUrl: './tenants-menu.component.html',
|
templateUrl: './tenants-menu.component.html',
|
||||||
styleUrls: ['./tenants-menu.component.scss'],
|
styleUrls: ['./tenants-menu.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class TenantsMenuComponent {
|
export class TenantsMenuComponent {
|
||||||
readonly tenantsService = inject(TenantsService);
|
readonly tenantsService = inject(TenantsService);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<div id="user-menu-items">
|
<div id="user-menu-items">
|
||||||
<ng-container *ngFor="let item of userMenuItems; trackBy: trackBy">
|
<ng-container *ngFor="let item of userMenuItems; trackBy: trackBy">
|
||||||
<a (click)="item.action?.()" *ngIf="item.show" [id]="item.id" [routerLink]="item.routerLink | tenant" mat-menu-item>
|
<a (click)="item.action?.()" *ngIf="item.show" [id]="item.id" [routerLink]="item.routerLink" mat-menu-item>
|
||||||
{{ item.name | translate }}
|
{{ item.name | translate }}
|
||||||
</a>
|
</a>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { User } from '@red/domain';
|
|||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
@ -21,12 +20,12 @@ interface MenuItem {
|
|||||||
selector: 'app-user-menu',
|
selector: 'app-user-menu',
|
||||||
templateUrl: './user-menu.component.html',
|
templateUrl: './user-menu.component.html',
|
||||||
styleUrls: ['./user-menu.component.scss'],
|
styleUrls: ['./user-menu.component.scss'],
|
||||||
|
standalone: false,
|
||||||
})
|
})
|
||||||
export class UserMenuComponent {
|
export class UserMenuComponent {
|
||||||
readonly #permissionsService = inject(IqserPermissionsService);
|
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
readonly tenantsService = inject(TenantsService);
|
|
||||||
readonly userService = inject(UserService);
|
readonly userService = inject(UserService);
|
||||||
|
readonly #permissionsService = inject(IqserPermissionsService);
|
||||||
readonly userMenuItems: List<MenuItem> = [
|
readonly userMenuItems: List<MenuItem> = [
|
||||||
{
|
{
|
||||||
id: 'account',
|
id: 'account',
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CanActivate } from '@angular/router';
|
import { CanActivate } from '@angular/router';
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
|
// TODO: Remove this and use a CanActivateFn instead
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class DashboardGuard implements CanActivate {
|
export class DashboardGuard implements CanActivate {
|
||||||
constructor(private readonly _dashboardStatsService: DashboardStatsService) {}
|
constructor(private readonly _dashboardStatsService: DashboardStatsService) {}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Injectable, Injector, ProviderToken } from '@angular/core';
|
import { Injectable, Injector, ProviderToken } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
|
||||||
import { DOSSIER_ID, DOSSIER_TEMPLATE_ID } from '@red/domain';
|
import { DOSSIER_ID, DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
@ -16,7 +15,6 @@ export class DossierFilesGuard implements CanActivate {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _injector: Injector,
|
private readonly _injector: Injector,
|
||||||
private readonly _tenantsService: TenantsService,
|
|
||||||
private readonly _filesMapService: FilesMapService,
|
private readonly _filesMapService: FilesMapService,
|
||||||
private readonly _filesService: FilesService,
|
private readonly _filesService: FilesService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -37,7 +35,7 @@ export class DossierFilesGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dossiersService.has(dossierId)) {
|
if (!dossiersService.has(dossierId)) {
|
||||||
await this._router.navigate([`/${this._tenantsService.activeTenantId}/main`, dossierTemplateId]);
|
await this._router.navigate(['/main', dossierTemplateId]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +47,7 @@ export class DossierFilesGuard implements CanActivate {
|
|||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
if (!this._dictionaryMapService.has(dossierId) && !this.isDocumine) {
|
if (!this._dictionaryMapService.has(dossierId) && !this.isDocumine) {
|
||||||
const dictionaryPromise = this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId);
|
const dictionaryPromise = firstValueFrom(this._dictionaryService.loadDictionaryDataForDossier(dossierTemplateId, dossierId));
|
||||||
promises.push(dictionaryPromise);
|
promises.push(dictionaryPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
|
||||||
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||||
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
@ -8,14 +7,35 @@ import { DossierTemplateStatsService } from '@services/entity-services/dossier-t
|
|||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
|
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||||
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
|
import { getConfig, Toaster } from '@iqser/common-ui';
|
||||||
|
import { RulesService } from '../modules/admin/services/rules.service';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
|
||||||
export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
||||||
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||||
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||||
|
const fileAttributesService = inject(FileAttributesService);
|
||||||
|
const dictionaryService = inject(DictionaryService);
|
||||||
|
const defaultColorsService = inject(DefaultColorsService);
|
||||||
|
const watermarksService = inject(WatermarkService);
|
||||||
|
const router = inject(Router);
|
||||||
|
const rulesService = inject(RulesService);
|
||||||
|
const isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
|
||||||
const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId);
|
const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId);
|
||||||
|
await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId));
|
||||||
|
await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId));
|
||||||
|
await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId));
|
||||||
|
await firstValueFrom(rulesService.getFor(dossierTemplateId));
|
||||||
|
if (!isDocumine) {
|
||||||
|
await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId));
|
||||||
|
}
|
||||||
if (!dossierTemplate) {
|
if (!dossierTemplate) {
|
||||||
await inject(Router).navigate([inject(TenantsService).activeTenantId, 'main', 'admin', 'dossier-templates']);
|
await router.navigate(['main', 'admin', 'dossier-templates']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -29,18 +49,34 @@ export function templateExistsWhenEnteringDossierList(): CanActivateFn {
|
|||||||
const dossierTemplatesService = inject(DossierTemplatesService);
|
const dossierTemplatesService = inject(DossierTemplatesService);
|
||||||
const logger = inject(NGXLogger);
|
const logger = inject(NGXLogger);
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
const tenantsService = inject(TenantsService);
|
|
||||||
const userPreferencesService = inject(UserPreferenceService);
|
const userPreferencesService = inject(UserPreferenceService);
|
||||||
|
const fileAttributesService = inject(FileAttributesService);
|
||||||
|
const dictionaryService = inject(DictionaryService);
|
||||||
|
const defaultColorsService = inject(DefaultColorsService);
|
||||||
|
const watermarksService = inject(WatermarkService);
|
||||||
|
const rulesService = inject(RulesService);
|
||||||
|
const toaster = inject(Toaster);
|
||||||
|
const isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
|
||||||
await firstValueFrom(dashboardStatsService.loadAll());
|
await firstValueFrom(dashboardStatsService.loadForTemplate(dossierTemplateId));
|
||||||
await firstValueFrom(dossierTemplatesService.loadAll());
|
await firstValueFrom(dossierTemplatesService.loadDossierTemplate(dossierTemplateId));
|
||||||
const dossierTemplateStats = dashboardStatsService.find(dossierTemplateId);
|
const dossierTemplateStats = dashboardStatsService.find(dossierTemplateId);
|
||||||
if (!dossierTemplateStats || dossierTemplateStats.isEmpty) {
|
if (!dossierTemplateStats || dossierTemplateStats.isEmpty) {
|
||||||
logger.warn(`[ROUTES] Dossier template ${dossierTemplateId} not found, redirecting to main`);
|
logger.warn(`[ROUTES] Dossier template ${dossierTemplateId} not found, redirecting to main`);
|
||||||
await userPreferencesService.saveLastDossierTemplate(null);
|
await userPreferencesService.saveLastDossierTemplate(null);
|
||||||
await router.navigate([tenantsService.activeTenantId, 'main']);
|
await router.navigate(['main']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId));
|
||||||
|
await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId));
|
||||||
|
await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId));
|
||||||
|
const rules = await firstValueFrom(rulesService.getFor(dossierTemplateId));
|
||||||
|
if (rules.timeoutDetected) {
|
||||||
|
toaster.error(_('dossier-listing.rules.timeoutError'));
|
||||||
|
}
|
||||||
|
if (!isDocumine) {
|
||||||
|
await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { CanDeactivateFn } from '@angular/router';
|
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import { CanDeactivateFn } from '@angular/router';
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
import { DossierOverviewScreenComponent } from '../modules/dossier-overview/screen/dossier-overview-screen.component';
|
import DossierOverviewScreenComponent from '../modules/dossier-overview/screen/dossier-overview-screen.component';
|
||||||
|
|
||||||
export const editAttributeGuard: CanDeactivateFn<DossierOverviewScreenComponent> = () =>
|
export const isNotEditingFileAttributeGuard: CanDeactivateFn<DossierOverviewScreenComponent> = () =>
|
||||||
!inject(FileAttributesService).isEditingFileAttribute();
|
!inject(FileAttributesService).isEditingFileAttribute();
|
||||||
@ -1,12 +1,13 @@
|
|||||||
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
||||||
import { keycloakInitializer, KeycloakStatusService, TenantsService } from '@iqser/common-ui/lib/tenants';
|
import { GET_TENANT_FROM_PATH_FN } from '@common-ui/utils';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
|
||||||
import { UserService } from '@users/user.service';
|
|
||||||
import { LicenseService } from '@services/license.service';
|
|
||||||
import { AsyncGuard } from '@iqser/common-ui';
|
import { AsyncGuard } from '@iqser/common-ui';
|
||||||
import jwt_decode from 'jwt-decode';
|
import { keycloakInitializer, KeycloakStatusService, TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||||
|
import { LicenseService } from '@services/license.service';
|
||||||
|
import { UserService } from '@users/user.service';
|
||||||
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
export interface JwtToken {
|
export interface JwtToken {
|
||||||
auth_time: number;
|
auth_time: number;
|
||||||
@ -24,35 +25,36 @@ export function ifLoggedIn(): AsyncGuard {
|
|||||||
const licenseService = inject(LicenseService);
|
const licenseService = inject(LicenseService);
|
||||||
const keycloakStatusService = inject(KeycloakStatusService);
|
const keycloakStatusService = inject(KeycloakStatusService);
|
||||||
|
|
||||||
|
const tenant = inject(GET_TENANT_FROM_PATH_FN)();
|
||||||
const keycloakInstance = keycloakService.getKeycloakInstance();
|
const keycloakInstance = keycloakService.getKeycloakInstance();
|
||||||
const tenant = route.paramMap.get('tenant');
|
|
||||||
const queryParams = new URLSearchParams(window.location.search);
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
const username = queryParams.get('username');
|
const username = queryParams.get('username');
|
||||||
|
const router = inject(Router);
|
||||||
|
|
||||||
if (!keycloakInstance) {
|
if (!keycloakInstance) {
|
||||||
if (!tenant) {
|
if (!tenant) {
|
||||||
logger.error('[ROUTES] No tenant found, something is wrong...');
|
logger.error('[ROUTES] No tenant found, something is wrong...');
|
||||||
return inject(Router).navigate(['/']);
|
return router.navigate(['/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('[KEYCLOAK] Keycloak init...');
|
logger.info('[KEYCLOAK] Keycloak init...');
|
||||||
await keycloakInitializer(tenant);
|
await keycloakInitializer(tenant);
|
||||||
logger.info('[KEYCLOAK] Keycloak init done!');
|
logger.info('[KEYCLOAK] Keycloak init done for tenant: ', { tenant });
|
||||||
await tenantsService.selectTenant(tenant);
|
await tenantsService.selectTenant(tenant);
|
||||||
await usersService.initialize();
|
await usersService.initialize();
|
||||||
await licenseService.loadLicenses();
|
await licenseService.loadLicenses();
|
||||||
|
|
||||||
const token = await keycloakService.getToken();
|
const token = await keycloakService.getToken();
|
||||||
if (token) {
|
if (token) {
|
||||||
const jwtToken = jwt_decode(token) as JwtToken;
|
const jwtToken = jwtDecode(token) as JwtToken;
|
||||||
const authTime = (jwtToken.auth_time || jwtToken.iat).toString();
|
const authTime = (jwtToken.auth_time || jwtToken.iat).toString();
|
||||||
localStorage.setItem('authTime', authTime);
|
localStorage.setItem('authTime', authTime);
|
||||||
|
localStorage.setItem('token', token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoggedIn = await keycloakService.isLoggedIn();
|
if (keycloakService.isLoggedIn()) {
|
||||||
|
|
||||||
if (isLoggedIn) {
|
|
||||||
logger.info('[ROUTES] Is logged in, continuing');
|
logger.info('[ROUTES] Is logged in, continuing');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
|
|
||||||
import { inject } from '@angular/core';
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
|
||||||
|
|
||||||
export function ifNotLoggedIn(): CanActivateFn {
|
|
||||||
return async (route: ActivatedRouteSnapshot) => {
|
|
||||||
const logger = inject(NGXLogger);
|
|
||||||
const router = inject(Router);
|
|
||||||
const keycloakService = inject(KeycloakService);
|
|
||||||
|
|
||||||
const isLoggedIn = await keycloakService.isLoggedIn();
|
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
|
||||||
logger.info('[ROUTES] Not logged in, continuing to selected route');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tenant = route.paramMap.get('tenant') || keycloakService.getKeycloakInstance().realm;
|
|
||||||
if (!tenant) {
|
|
||||||
logger.error('[ROUTES] Tenant not found in route or keycloak realm');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.warn('[ROUTES] Is logged in for ' + tenant + ', redirecting to /' + tenant);
|
|
||||||
await router.navigate([tenant]);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { EntityPermissionsService } from '@services/entity-permissions/entity-permissions.service';
|
import { EntityPermissionsService } from '@services/entity-permissions/entity-permissions.service';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class PermissionsGuard implements CanActivate {
|
export class PermissionsGuard implements CanActivate {
|
||||||
|
|||||||
@ -1,48 +1,73 @@
|
|||||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary, SuperTypes } from '@red/domain';
|
||||||
|
|
||||||
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) =>
|
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) => !isApprover && annotation.pending;
|
||||||
!isApprover && (annotation.isSuggestion || annotation.pending);
|
|
||||||
|
|
||||||
export const canForceHint = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canForceHint = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||||
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
||||||
|
|
||||||
export const canForceRedaction = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canForceRedaction = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||||
canAddRedaction && annotation.isSkipped && !annotation.isFalsePositive && !annotation.pending;
|
canAddRedaction &&
|
||||||
|
(annotation.isSkipped || (annotation.IMAGE_HINT && annotation.superType === SuperTypes.Hint)) &&
|
||||||
|
!annotation.isFalsePositive &&
|
||||||
|
!annotation.pending;
|
||||||
|
|
||||||
export const canAcceptRecommendation = (annotation: AnnotationWrapper) => annotation.isRecommendation && !annotation.pending;
|
export const canAcceptRecommendation = (annotation: AnnotationWrapper) => annotation.isRecommendation && !annotation.pending;
|
||||||
|
|
||||||
export const canMarkAsFalsePositive = (annotation: AnnotationWrapper, annotationEntity: Dictionary) =>
|
export const canMarkAsFalsePositive = (annotation: AnnotationWrapper, annotationEntity: Dictionary) =>
|
||||||
annotation.canBeMarkedAsFalsePositive && annotationEntity.hasDictionary;
|
annotation.canBeMarkedAsFalsePositive && !annotation.hasBeenResizedLocally && annotationEntity?.hasDictionary;
|
||||||
|
|
||||||
export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
||||||
canAddRedaction && !annotation.pending && (annotation.isRedacted || (annotation.isHint && !annotation.isImage));
|
canAddRedaction &&
|
||||||
|
(autoAnalysisDisabled || !annotation.pending) &&
|
||||||
|
(annotation.isRedacted || (annotation.isHint && !annotation.isImage));
|
||||||
|
|
||||||
export const canRemoveFromDictionary = (annotation: AnnotationWrapper) =>
|
export const canRemoveFromDictionary = (annotation: AnnotationWrapper, autoAnalysisDisabled: boolean) =>
|
||||||
annotation.isModifyDictionary &&
|
(annotation.isModifyDictionary || annotation.engines.includes('DICTIONARY') || annotation.engines.includes('DOSSIER_DICTIONARY')) &&
|
||||||
(annotation.isRedacted || annotation.isSkipped || annotation.isHint) &&
|
(annotation.isRedacted || annotation.isSkipped || annotation.isHint || (annotation.isIgnoredHint && !annotation.isRuleBased)) &&
|
||||||
!annotation.pending &&
|
(autoAnalysisDisabled || !annotation.pending) &&
|
||||||
!annotation.hasBeenResized;
|
annotation.isDictBased;
|
||||||
|
|
||||||
export const canRemoveRedaction = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
export const canRemoveRedaction = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
||||||
!annotation.isIgnoredHint &&
|
(!annotation.isIgnoredHint || !annotation.isRuleBased) &&
|
||||||
(permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive);
|
(permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive);
|
||||||
|
|
||||||
export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
||||||
canAddRedaction && annotation.isRedacted && !annotation.pending;
|
canAddRedaction && annotation.isRedacted && (autoAnalysisDisabled || !annotation.pending);
|
||||||
|
|
||||||
export const canRecategorizeAnnotation = (annotation: AnnotationWrapper, canRecategorize: boolean) =>
|
export const canRecategorizeAnnotation = (annotation: AnnotationWrapper, canRecategorize: boolean, autoAnalysisDisabled: boolean) =>
|
||||||
canRecategorize &&
|
canRecategorize &&
|
||||||
((annotation.isImage && !annotation.isSuggestion) || annotation.isSuggestionRecategorizeImage || annotation.hintDictionary) &&
|
(annotation.isImage || (annotation.isHint && !annotation.isRuleBased)) &&
|
||||||
!annotation.pending;
|
(autoAnalysisDisabled || !annotation.pending);
|
||||||
|
|
||||||
export const canResizeAnnotation = (annotation: AnnotationWrapper, canAddRedaction: boolean, hasDictionary = false) =>
|
export const canResizeAnnotation = (
|
||||||
canAddRedaction &&
|
annotation: AnnotationWrapper,
|
||||||
!annotation.isSkipped &&
|
canAddRedaction: boolean,
|
||||||
!annotation.pending &&
|
autoAnalysisDisabled: boolean,
|
||||||
(((annotation.isRedacted || annotation.isImage) && !annotation.isSuggestion) ||
|
hasDictionary = false,
|
||||||
annotation.isSuggestionResize ||
|
) =>
|
||||||
annotation.isDictBasedHint ||
|
(canAddRedaction &&
|
||||||
annotation.isRecommendation ||
|
!annotation.isSkipped &&
|
||||||
(hasDictionary && annotation.isRuleBased));
|
!annotation.isIgnoredHint &&
|
||||||
|
(autoAnalysisDisabled || !annotation.pending) &&
|
||||||
|
(annotation.isRedacted ||
|
||||||
|
annotation.isImage ||
|
||||||
|
(annotation.isHint && !annotation.isRuleBased) ||
|
||||||
|
(!annotation.isHint && hasDictionary && annotation.isRuleBased))) ||
|
||||||
|
annotation.isRecommendation;
|
||||||
|
|
||||||
|
export const canResizeInDictionary = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
||||||
|
permissions.canResizeAnnotation &&
|
||||||
|
annotation.isModifyDictionary &&
|
||||||
|
!annotation.hasBeenResizedLocally &&
|
||||||
|
!(annotation.hasBeenForcedHint || annotation.hasBeenForcedRedaction);
|
||||||
|
|
||||||
|
export const canEditAnnotation = (annotation: AnnotationWrapper) => (annotation.isRedacted || annotation.isSkipped) && !annotation.isImage;
|
||||||
|
|
||||||
|
export const canEditHint = (annotation: AnnotationWrapper) =>
|
||||||
|
((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage;
|
||||||
|
|
||||||
|
export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage;
|
||||||
|
|
||||||
|
export const canRevertChanges = (annotation: AnnotationWrapper) => annotation.hasRedactionChanges;
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import { IqserPermissionsService } from '@iqser/common-ui';
|
|||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary } from '@red/domain';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { isArray } from 'lodash-es';
|
import { isArray } from 'lodash-es';
|
||||||
import { IMAGE_CATEGORIES } from '../../modules/file-preview/utils/constants';
|
|
||||||
import {
|
import {
|
||||||
canAcceptRecommendation,
|
canAcceptRecommendation,
|
||||||
canChangeLegalBasis,
|
canChangeLegalBasis,
|
||||||
|
canEditAnnotation,
|
||||||
|
canEditHint,
|
||||||
|
canEditImage,
|
||||||
canForceHint,
|
canForceHint,
|
||||||
canForceRedaction,
|
canForceRedaction,
|
||||||
canMarkAsFalsePositive,
|
canMarkAsFalsePositive,
|
||||||
@ -14,6 +16,8 @@ import {
|
|||||||
canRemoveOnlyHere,
|
canRemoveOnlyHere,
|
||||||
canRemoveRedaction,
|
canRemoveRedaction,
|
||||||
canResizeAnnotation,
|
canResizeAnnotation,
|
||||||
|
canResizeInDictionary,
|
||||||
|
canRevertChanges,
|
||||||
canUndo,
|
canUndo,
|
||||||
} from './annotation-permissions.utils';
|
} from './annotation-permissions.utils';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
@ -28,17 +32,20 @@ export class AnnotationPermissions {
|
|||||||
canForceRedaction = true;
|
canForceRedaction = true;
|
||||||
canChangeLegalBasis = true;
|
canChangeLegalBasis = true;
|
||||||
canResizeAnnotation = true;
|
canResizeAnnotation = true;
|
||||||
|
canResizeInDictionary = true;
|
||||||
canRecategorizeAnnotation = true;
|
canRecategorizeAnnotation = true;
|
||||||
canForceHint = true;
|
canForceHint = true;
|
||||||
canEditAnnotations = true;
|
canEditAnnotations = true;
|
||||||
canEditHints = true;
|
canEditHints = true;
|
||||||
canEditImages = true;
|
canEditImages = true;
|
||||||
|
canRevertChanges = true;
|
||||||
|
|
||||||
static forUser(
|
static forUser(
|
||||||
isApprover: boolean,
|
isApprover: boolean,
|
||||||
annotations: AnnotationWrapper | AnnotationWrapper[],
|
annotations: AnnotationWrapper | AnnotationWrapper[],
|
||||||
entities: Dictionary[],
|
entities: Dictionary[],
|
||||||
permissionsService: IqserPermissionsService,
|
permissionsService: IqserPermissionsService,
|
||||||
|
autoAnalysisDisabled: boolean,
|
||||||
) {
|
) {
|
||||||
if (!isArray(annotations)) {
|
if (!isArray(annotations)) {
|
||||||
annotations = [annotations];
|
annotations = [annotations];
|
||||||
@ -55,18 +62,22 @@ export class AnnotationPermissions {
|
|||||||
permissions.canForceRedaction = canForceRedaction(annotation, canAddRedaction);
|
permissions.canForceRedaction = canForceRedaction(annotation, canAddRedaction);
|
||||||
permissions.canAcceptRecommendation = canAcceptRecommendation(annotation);
|
permissions.canAcceptRecommendation = canAcceptRecommendation(annotation);
|
||||||
permissions.canMarkAsFalsePositive = canMarkAsFalsePositive(annotation, annotationEntity);
|
permissions.canMarkAsFalsePositive = canMarkAsFalsePositive(annotation, annotationEntity);
|
||||||
permissions.canRemoveOnlyHere = canRemoveOnlyHere(annotation, canAddRedaction);
|
permissions.canRemoveOnlyHere = canRemoveOnlyHere(annotation, canAddRedaction, autoAnalysisDisabled);
|
||||||
permissions.canRemoveFromDictionary = canRemoveFromDictionary(annotation);
|
permissions.canRemoveFromDictionary = canRemoveFromDictionary(annotation, autoAnalysisDisabled);
|
||||||
permissions.canRemoveRedaction = canRemoveRedaction(annotation, permissions);
|
permissions.canRemoveRedaction = canRemoveRedaction(annotation, permissions);
|
||||||
permissions.canChangeLegalBasis = canChangeLegalBasis(annotation, canAddRedaction);
|
permissions.canChangeLegalBasis = canChangeLegalBasis(annotation, canAddRedaction, autoAnalysisDisabled);
|
||||||
permissions.canRecategorizeAnnotation = canRecategorizeAnnotation(annotation, canAddRedaction);
|
permissions.canRecategorizeAnnotation = canRecategorizeAnnotation(annotation, canAddRedaction, autoAnalysisDisabled);
|
||||||
permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction, annotationEntity.hasDictionary);
|
permissions.canResizeAnnotation = canResizeAnnotation(
|
||||||
|
annotation,
|
||||||
permissions.canEditAnnotations = (annotation.isSkipped || annotation.isRedacted) && !annotation.isImage;
|
canAddRedaction,
|
||||||
permissions.canEditHints =
|
autoAnalysisDisabled,
|
||||||
annotation.isIgnoredHint || annotation.isDictBasedHint || annotation.isHint || annotation.isSuggestionForceHint;
|
annotationEntity?.hasDictionary,
|
||||||
permissions.canEditImages = [...IMAGE_CATEGORIES, 'ocr'].includes(annotation.type);
|
);
|
||||||
|
permissions.canResizeInDictionary = canResizeInDictionary(annotation, permissions);
|
||||||
|
permissions.canEditAnnotations = canEditAnnotation(annotation);
|
||||||
|
permissions.canEditHints = canEditHint(annotation);
|
||||||
|
permissions.canEditImages = canEditImage(annotation);
|
||||||
|
permissions.canRevertChanges = canRevertChanges(annotation);
|
||||||
summedPermissions._merge(permissions);
|
summedPermissions._merge(permissions);
|
||||||
}
|
}
|
||||||
return summedPermissions;
|
return summedPermissions;
|
||||||
@ -75,6 +86,7 @@ export class AnnotationPermissions {
|
|||||||
static reduce(permissions: AnnotationPermissions[]): AnnotationPermissions {
|
static reduce(permissions: AnnotationPermissions[]): AnnotationPermissions {
|
||||||
const result = new AnnotationPermissions();
|
const result = new AnnotationPermissions();
|
||||||
result.canResizeAnnotation = permissions.length === 1 && permissions[0].canResizeAnnotation;
|
result.canResizeAnnotation = permissions.length === 1 && permissions[0].canResizeAnnotation;
|
||||||
|
result.canResizeInDictionary = permissions.length === 1 && permissions[0].canResizeInDictionary;
|
||||||
result.canChangeLegalBasis = permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true);
|
result.canChangeLegalBasis = permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true);
|
||||||
result.canRecategorizeAnnotation = permissions.reduce((acc, next) => acc && next.canRecategorizeAnnotation, true);
|
result.canRecategorizeAnnotation = permissions.reduce((acc, next) => acc && next.canRecategorizeAnnotation, true);
|
||||||
result.canRemoveFromDictionary = permissions.reduce((acc, next) => acc && next.canRemoveFromDictionary, true);
|
result.canRemoveFromDictionary = permissions.reduce((acc, next) => acc && next.canRemoveFromDictionary, true);
|
||||||
@ -88,6 +100,7 @@ export class AnnotationPermissions {
|
|||||||
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
||||||
result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true);
|
result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true);
|
||||||
result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true);
|
result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true);
|
||||||
|
result.canRevertChanges = permissions.reduce((acc, next) => acc && next.canRevertChanges, true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,83 +5,99 @@ import {
|
|||||||
annotationEntityColorConfig,
|
annotationEntityColorConfig,
|
||||||
AnnotationIconType,
|
AnnotationIconType,
|
||||||
ChangeType,
|
ChangeType,
|
||||||
ChangeTypes,
|
|
||||||
DefaultColors,
|
DefaultColors,
|
||||||
Dictionary,
|
Dictionary,
|
||||||
Earmark,
|
Earmark,
|
||||||
|
EntityTypes,
|
||||||
|
EntryStates,
|
||||||
FalsePositiveSuperTypes,
|
FalsePositiveSuperTypes,
|
||||||
|
IEntityLogEntry,
|
||||||
ILegalBasis,
|
ILegalBasis,
|
||||||
IManualChange,
|
|
||||||
IPoint,
|
IPoint,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
IRedactionLogEntry,
|
|
||||||
LogEntryEngine,
|
LogEntryEngine,
|
||||||
LogEntryEngines,
|
LogEntryEngines,
|
||||||
LogEntryStatuses,
|
|
||||||
LowLevelFilterTypes,
|
LowLevelFilterTypes,
|
||||||
|
ManualRedactionType,
|
||||||
ManualRedactionTypes,
|
ManualRedactionTypes,
|
||||||
SuggestionAddSuperTypes,
|
|
||||||
SuggestionRemoveSuperTypes,
|
|
||||||
SuggestionsSuperTypes,
|
|
||||||
SuperType,
|
SuperType,
|
||||||
|
SuperTypeMapper,
|
||||||
SuperTypes,
|
SuperTypes,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { annotationTypesTranslations, SuggestionAddFalsePositive } from '@translations/annotation-types-translations';
|
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
||||||
import { chronologicallyBy, timestampOf } from '../../modules/file-preview/services/file-data.service';
|
|
||||||
|
interface AnnotationContent {
|
||||||
|
translation: string;
|
||||||
|
params: { [key: string]: string };
|
||||||
|
untranslatedContent: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class AnnotationWrapper implements IListable {
|
export class AnnotationWrapper implements IListable {
|
||||||
[x: string]: unknown;
|
|
||||||
|
|
||||||
superType: SuperType;
|
|
||||||
typeValue: string;
|
|
||||||
recategorizationType: string;
|
|
||||||
color: string;
|
|
||||||
entity: Dictionary;
|
|
||||||
numberOfComments = 0;
|
|
||||||
firstTopLeftPoint: IPoint;
|
|
||||||
id: string;
|
id: string;
|
||||||
|
superType: SuperType;
|
||||||
|
superTypeLabel: string;
|
||||||
|
type: string;
|
||||||
|
typeLabel?: string;
|
||||||
|
color: string;
|
||||||
|
numberOfComments = 0;
|
||||||
|
firstBottomLeftPoint: IPoint;
|
||||||
shortContent: string;
|
shortContent: string;
|
||||||
content: string;
|
content: AnnotationContent;
|
||||||
value: string;
|
value: string;
|
||||||
typeLabel: string;
|
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
hint: boolean;
|
dictionaryOperation = false;
|
||||||
redaction: boolean;
|
positions: IRectangle[] = [];
|
||||||
status: string;
|
|
||||||
dictionaryOperation: boolean;
|
|
||||||
positions: IRectangle[];
|
|
||||||
recommendationType: string;
|
|
||||||
legalBasisValue: string;
|
legalBasisValue: string;
|
||||||
legalBasisChangeValue?: string;
|
// AREA === rectangle
|
||||||
rectangle?: boolean;
|
AREA = false;
|
||||||
|
HINT = false;
|
||||||
|
IMAGE = false;
|
||||||
|
IMAGE_HINT = false;
|
||||||
section?: string;
|
section?: string;
|
||||||
reference: string[];
|
reference: string[] = [];
|
||||||
imported?: boolean;
|
imported = false;
|
||||||
image?: boolean;
|
manual = false;
|
||||||
manual?: boolean;
|
|
||||||
pending = false;
|
pending = false;
|
||||||
hintDictionary = false;
|
|
||||||
textAfter?: string;
|
textAfter?: string;
|
||||||
textBefore?: string;
|
textBefore?: string;
|
||||||
isChangeLogEntry = false;
|
isChangeLogEntry = false;
|
||||||
changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
|
engines: LogEntryEngine[] = [];
|
||||||
engines?: LogEntryEngine[];
|
|
||||||
hasBeenResized: boolean;
|
hasBeenResized: boolean;
|
||||||
|
hasBeenResizedLocally: boolean;
|
||||||
hasBeenRecategorized: boolean;
|
hasBeenRecategorized: boolean;
|
||||||
hasLegalBasisChanged: boolean;
|
hasLegalBasisChanged: boolean;
|
||||||
hasBeenForcedHint: boolean;
|
hasBeenForcedHint: boolean;
|
||||||
hasBeenForcedRedaction: boolean;
|
hasBeenForcedRedaction: boolean;
|
||||||
hasBeenRemovedByManualOverride: boolean;
|
hasBeenRemovedByManualOverride: boolean;
|
||||||
legalBasisList: ILegalBasis[] = [];
|
isRemoved = false;
|
||||||
|
isRemovedLocally = false;
|
||||||
|
hiddenInWorkload = false;
|
||||||
|
lastManualChange: ManualRedactionType;
|
||||||
|
entry: IEntityLogEntry;
|
||||||
|
|
||||||
|
get isRuleBased() {
|
||||||
|
return this.engines.includes(LogEntryEngines.RULE);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isDictBased() {
|
||||||
|
return [LogEntryEngines.DICTIONARY, LogEntryEngines.DOSSIER_DICTIONARY].some(engine => this.engines.includes(engine));
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRedactedImageHint() {
|
||||||
|
return (
|
||||||
|
(this.IMAGE_HINT && this.superType === SuperTypes.Redaction) ||
|
||||||
|
(this.IMAGE_HINT && this.superType === SuperTypes.ManualRedaction)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isSkippedImageHint() {
|
||||||
|
return this.IMAGE_HINT && this.superType === SuperTypes.Hint;
|
||||||
|
}
|
||||||
|
|
||||||
get searchKey(): string {
|
get searchKey(): string {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isChangeLogRemoved() {
|
|
||||||
return this.changeLogType === 'REMOVED';
|
|
||||||
}
|
|
||||||
|
|
||||||
get descriptor() {
|
get descriptor() {
|
||||||
return this.isModifyDictionary ? _('dictionary') : _('type');
|
return this.isModifyDictionary ? _('dictionary') : _('type');
|
||||||
}
|
}
|
||||||
@ -96,7 +112,10 @@ export class AnnotationWrapper implements IListable {
|
|||||||
|
|
||||||
get canBeMarkedAsFalsePositive() {
|
get canBeMarkedAsFalsePositive() {
|
||||||
return (
|
return (
|
||||||
(this.isRecommendation || this.superType === SuperTypes.Redaction) &&
|
(this.isRecommendation ||
|
||||||
|
this.superType === SuperTypes.Redaction ||
|
||||||
|
(this.isSkipped && this.isDictBased) ||
|
||||||
|
(this.isRemovedLocally && this.isDictBased)) &&
|
||||||
!this.isImage &&
|
!this.isImage &&
|
||||||
!this.imported &&
|
!this.imported &&
|
||||||
!this.pending &&
|
!this.pending &&
|
||||||
@ -104,30 +123,18 @@ export class AnnotationWrapper implements IListable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuperTypeBasedColor() {
|
|
||||||
return this.isSuggestion || this.isDeclinedSuggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSkipped() {
|
get isSkipped() {
|
||||||
return this.superType === SuperTypes.Skipped;
|
return this.superType === SuperTypes.Skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isImage() {
|
get isImage() {
|
||||||
return this.type?.toLowerCase() === 'image' || this.image;
|
return this.type?.toLowerCase() === 'image' || this.IMAGE || this.IMAGE_HINT;
|
||||||
}
|
|
||||||
|
|
||||||
get isNotSignatureImage() {
|
|
||||||
return this.isImage && this.recategorizationType === 'signature';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOCR() {
|
get isOCR() {
|
||||||
return this.type?.toLowerCase() === 'ocr';
|
return this.type?.toLowerCase() === 'ocr';
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this.recategorizationType || this.typeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
get topLevelFilter() {
|
get topLevelFilter() {
|
||||||
return !LowLevelFilterTypes[this.superType];
|
return !LowLevelFilterTypes[this.superType];
|
||||||
}
|
}
|
||||||
@ -144,20 +151,12 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
|
return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestionAddToFalsePositive() {
|
|
||||||
return this.typeLabel === annotationTypesTranslations[SuggestionAddFalsePositive];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDeclinedSuggestion() {
|
|
||||||
return this.superType === SuperTypes.DeclinedSuggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isApproved() {
|
|
||||||
return this.status === 'APPROVED';
|
|
||||||
}
|
|
||||||
|
|
||||||
get isHint() {
|
get isHint() {
|
||||||
return this.superType === SuperTypes.Hint;
|
return this.superType === SuperTypes.Hint || this.superType === SuperTypes.ManualHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isIgnoredHint() {
|
||||||
|
return this.superType === SuperTypes.IgnoredHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isEarmark() {
|
get isEarmark() {
|
||||||
@ -169,69 +168,17 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return 'hexagon';
|
return 'hexagon';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isHint || this.isIgnoredHint) {
|
if (this.HINT) {
|
||||||
return 'circle';
|
return 'circle';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isSuggestion || this.isDeclinedSuggestion) {
|
|
||||||
return 'rhombus';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'square';
|
return 'square';
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDictBasedHint() {
|
|
||||||
return this.isHint && this.engines.includes(LogEntryEngines.DICTIONARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isIgnoredHint() {
|
|
||||||
return this.superType === SuperTypes.IgnoredHint;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRedacted() {
|
get isRedacted() {
|
||||||
return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
|
return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestion() {
|
|
||||||
return !!SuggestionsSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRuleBased() {
|
|
||||||
return this.engines.includes(LogEntryEngines.RULE);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionResize() {
|
|
||||||
return this.superType === SuperTypes.SuggestionResize;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRecategorizeImage() {
|
|
||||||
return this.superType === SuperTypes.SuggestionRecategorizeImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionForceHint() {
|
|
||||||
return this.superType === SuperTypes.SuggestionForceHint;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionAdd() {
|
|
||||||
return !!SuggestionAddSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionAddDictionary() {
|
|
||||||
return this.superType === SuperTypes.SuggestionAddDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRemove() {
|
|
||||||
return !!SuggestionRemoveSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRemoveDictionary() {
|
|
||||||
return this.superType === SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionLegalBasisChange() {
|
|
||||||
return this.superType === SuperTypes.SuggestionChangeLegalBasis;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isModifyDictionary() {
|
get isModifyDictionary() {
|
||||||
return this.dictionaryOperation;
|
return this.dictionaryOperation;
|
||||||
}
|
}
|
||||||
@ -252,15 +199,15 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get x() {
|
get x() {
|
||||||
return this.firstTopLeftPoint.x;
|
return this.firstBottomLeftPoint.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
get y() {
|
get y() {
|
||||||
return this.firstTopLeftPoint.y;
|
return this.firstBottomLeftPoint.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
get legalBasis() {
|
get legalBasis() {
|
||||||
return this.legalBasisChangeValue || this.legalBasisValue;
|
return this.legalBasisValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
get width(): number {
|
get width(): number {
|
||||||
@ -271,201 +218,167 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return Math.floor(this.positions[0].height);
|
return Math.floor(this.positions[0].height);
|
||||||
}
|
}
|
||||||
|
|
||||||
get previewAnnotation() {
|
|
||||||
return (
|
|
||||||
this.isRedacted ||
|
|
||||||
this.isSuggestionAdd ||
|
|
||||||
this.isSuggestionRemove ||
|
|
||||||
this.isSuggestionResize ||
|
|
||||||
this.isSuggestionLegalBasisChange ||
|
|
||||||
this.isSuggestionRecategorizeImage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromEarmark(earmark: Earmark) {
|
static fromEarmark(earmark: Earmark) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.id = earmark.id;
|
annotationWrapper.id = earmark.id;
|
||||||
annotationWrapper.pageNumber = earmark.positions[0].page;
|
annotationWrapper.pageNumber = earmark.positions[0].page;
|
||||||
annotationWrapper.superType = SuperTypes.TextHighlight;
|
annotationWrapper.superType = SuperTypes.TextHighlight;
|
||||||
annotationWrapper.typeValue = SuperTypes.TextHighlight;
|
annotationWrapper.type = SuperTypes.TextHighlight;
|
||||||
annotationWrapper.value = 'Imported';
|
annotationWrapper.value = 'Imported';
|
||||||
annotationWrapper.color = earmark.hexColor;
|
annotationWrapper.color = earmark.hexColor;
|
||||||
annotationWrapper.positions = earmark.positions;
|
annotationWrapper.positions = earmark.positions;
|
||||||
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
|
annotationWrapper.firstBottomLeftPoint = earmark.positions[0]?.topLeft;
|
||||||
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromData(
|
static fromData(
|
||||||
redactionLogEntry: IRedactionLogEntry,
|
logEntry: IEntityLogEntry,
|
||||||
dictionaries: Dictionary[],
|
dictionary: Dictionary,
|
||||||
defaultColors: DefaultColors,
|
|
||||||
changeLogType: ChangeType,
|
changeLogType: ChangeType,
|
||||||
legalBasisList: ILegalBasis[],
|
legalBasisList: ILegalBasis[],
|
||||||
hintDictionary: boolean,
|
|
||||||
isDocumine: boolean,
|
isDocumine: boolean,
|
||||||
|
defaultColors: DefaultColors,
|
||||||
) {
|
) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.id = redactionLogEntry.id;
|
annotationWrapper.pending = logEntry.state === EntryStates.PENDING;
|
||||||
annotationWrapper.isChangeLogEntry = !!changeLogType;
|
annotationWrapper.id = logEntry.id + (annotationWrapper.pending ? '-pending' : '');
|
||||||
annotationWrapper.changeLogType = changeLogType;
|
annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
|
||||||
annotationWrapper.legalBasisList = legalBasisList;
|
annotationWrapper.type = logEntry.type;
|
||||||
annotationWrapper.redaction = redactionLogEntry.redacted;
|
annotationWrapper.value = logEntry.value;
|
||||||
annotationWrapper.hint = redactionLogEntry.hint;
|
annotationWrapper.firstBottomLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
|
||||||
annotationWrapper.typeValue = redactionLogEntry.type;
|
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
|
||||||
annotationWrapper.value = redactionLogEntry.value;
|
annotationWrapper.positions = logEntry.positions.map(p => ({
|
||||||
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
|
page: p.pageNumber,
|
||||||
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
|
height: p.rectangle[3],
|
||||||
annotationWrapper.positions = redactionLogEntry.positions;
|
width: p.rectangle[2],
|
||||||
annotationWrapper.textBefore = redactionLogEntry.textBefore;
|
topLeft: { x: p.rectangle[0], y: p.rectangle[1] },
|
||||||
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
}));
|
||||||
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
annotationWrapper.textBefore = logEntry.textBefore;
|
||||||
annotationWrapper.image = redactionLogEntry.image;
|
annotationWrapper.textAfter = logEntry.textAfter;
|
||||||
annotationWrapper.imported = redactionLogEntry.imported;
|
annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;
|
||||||
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
|
|
||||||
annotationWrapper.manual = redactionLogEntry.manualChanges?.length > 0;
|
|
||||||
annotationWrapper.engines = redactionLogEntry.engines;
|
|
||||||
annotationWrapper.section = redactionLogEntry.section;
|
|
||||||
annotationWrapper.reference = redactionLogEntry.reference || [];
|
|
||||||
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
|
||||||
annotationWrapper.hintDictionary = hintDictionary;
|
|
||||||
annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.legalBasisChangeValue = redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
|
|
||||||
)?.propertyChanges.legalBasis;
|
|
||||||
|
|
||||||
this.#createContent(annotationWrapper, redactionLogEntry, isDocumine);
|
annotationWrapper.HINT = logEntry.entryType === EntityTypes.HINT;
|
||||||
this.#setSuperType(annotationWrapper, redactionLogEntry);
|
annotationWrapper.IMAGE = logEntry.entryType === EntityTypes.IMAGE;
|
||||||
this.#handleRecommendations(annotationWrapper, redactionLogEntry);
|
annotationWrapper.AREA = logEntry.entryType === EntityTypes.AREA;
|
||||||
annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);
|
annotationWrapper.IMAGE_HINT = logEntry.entryType === EntityTypes.IMAGE_HINT;
|
||||||
|
|
||||||
const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
|
annotationWrapper.numberOfComments = logEntry.numberOfComments;
|
||||||
annotationWrapper.entity = entity?.virtual ? null : entity;
|
annotationWrapper.imported = logEntry.imported;
|
||||||
|
annotationWrapper.legalBasisValue = logEntry.legalBasis;
|
||||||
|
annotationWrapper.manual = logEntry.manualChanges?.length > 0;
|
||||||
|
annotationWrapper.engines = logEntry.engines ?? [];
|
||||||
|
annotationWrapper.section = logEntry.section;
|
||||||
|
annotationWrapper.reference = logEntry.reference || [];
|
||||||
|
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
|
||||||
|
annotationWrapper.hasBeenResizedLocally =
|
||||||
|
annotationWrapper.hasBeenResized && annotationWrapper.engines.includes(LogEntryEngines.MANUAL);
|
||||||
|
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasBeenForcedHint =
|
||||||
|
!!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && annotationWrapper.HINT;
|
||||||
|
annotationWrapper.hasBeenForcedRedaction =
|
||||||
|
!!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && !annotationWrapper.HINT;
|
||||||
|
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.REMOVE,
|
||||||
|
);
|
||||||
|
|
||||||
let colorKey = annotationWrapper.isSuperTypeBasedColor
|
const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
|
||||||
? annotationDefaultColorConfig[annotationWrapper.superType]
|
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content.untranslatedContent;
|
||||||
: annotationEntityColorConfig[annotationWrapper.superType];
|
annotationWrapper.content = content;
|
||||||
|
|
||||||
if (annotationWrapper.isSuperTypeBasedColor && annotationWrapper.isSuggestionResize && !annotationWrapper.isModifyDictionary) {
|
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
|
||||||
colorKey = annotationDefaultColorConfig[SuperTypes.SuggestionAdd];
|
annotationWrapper.lastManualChange = lastRelevantManualChange?.manualRedactionType;
|
||||||
|
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
|
||||||
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
|
annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED;
|
||||||
|
annotationWrapper.isRemovedLocally =
|
||||||
|
lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE &&
|
||||||
|
logEntry.engines.includes(LogEntryEngines.MANUAL);
|
||||||
|
|
||||||
|
annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label;
|
||||||
|
|
||||||
|
if (annotationWrapper.pending) {
|
||||||
|
annotationWrapper.color = defaultColors[annotationDefaultColorConfig.analysis] as string;
|
||||||
|
} else {
|
||||||
|
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
|
||||||
|
const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType];
|
||||||
|
annotationWrapper.color =
|
||||||
|
dictionary && !annotationWrapper.isRedactedImageHint
|
||||||
|
? (dictionary[colorKey] as string)
|
||||||
|
: (defaultColors[defaultColor] as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.color = annotationWrapper.isSuperTypeBasedColor ? defaultColors[colorKey] : (entity[colorKey] as string);
|
annotationWrapper.entry = logEntry;
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getTypeLabel(redactionLogEntry: IRedactionLogEntry, annotation: AnnotationWrapper): string {
|
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
|
||||||
if (redactionLogEntry.reason?.toLowerCase() === 'false positive') {
|
let untranslatedContent = '';
|
||||||
return annotationTypesTranslations[SuggestionAddFalsePositive];
|
const params: { [key: string]: string } = {};
|
||||||
}
|
if (logEntry.matchedRule) {
|
||||||
if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
|
params['hasRule'] = 'true';
|
||||||
return _('annotation-type.manual-hint');
|
params['matchedRule'] = logEntry.matchedRule.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
}
|
params['ruleSymbol'] = isDocumine ? ':' : '';
|
||||||
return annotationTypesTranslations[annotation.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
static #handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: IRedactionLogEntry) {
|
untranslatedContent += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
||||||
if (annotationWrapper.superType === SuperTypes.Recommendation) {
|
|
||||||
annotationWrapper.recommendationType = redactionLogEntry.type;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static #setSuperType(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry) {
|
if (logEntry.reason) {
|
||||||
if (entry.manualChanges?.length) {
|
params['hasReason'] = 'true';
|
||||||
const lastRelevantManualChange = entry.manualChanges?.at(-1);
|
if (isDocumine && logEntry.reason.slice(-1) === '.') {
|
||||||
const viableChanges = entry.changes.filter(c => c.analysisNumber > 1);
|
logEntry.reason = logEntry.reason.slice(0, -1);
|
||||||
const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
|
||||||
const lastChangeOccurredAfterLastManualChange =
|
|
||||||
lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);
|
|
||||||
|
|
||||||
if (lastChangeOccurredAfterLastManualChange && lastChange.type === ChangeTypes.ADDED && entry.redacted) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if (!params['hasRule']) {
|
||||||
annotationWrapper.pending = !lastRelevantManualChange.processed;
|
params['reason'] = logEntry.reason.substring(0, 1).toUpperCase() + logEntry.reason.substring(1);
|
||||||
|
|
||||||
annotationWrapper.superType = this.#selectSuperType(entry, lastRelevantManualChange, annotationWrapper.hintDictionary);
|
|
||||||
|
|
||||||
if (lastRelevantManualChange.annotationStatus === LogEntryStatuses.REQUESTED) {
|
|
||||||
annotationWrapper.recategorizationType = lastRelevantManualChange.propertyChanges.type;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (entry.recommendation) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Recommendation;
|
|
||||||
} else if (entry.redacted) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
|
||||||
} else if (entry.hint) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Hint;
|
|
||||||
} else {
|
} else {
|
||||||
annotationWrapper.superType = SuperTypes.Skipped;
|
params['reason'] = logEntry.reason;
|
||||||
}
|
}
|
||||||
}
|
params['reason'] = params['reason'].replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
}
|
untranslatedContent += logEntry.reason + '\n\n';
|
||||||
|
|
||||||
static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry, isDocumine: boolean) {
|
|
||||||
let content = '';
|
|
||||||
if (entry.matchedRule) {
|
|
||||||
content += `Rule ${entry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.reason) {
|
|
||||||
if (isDocumine && entry.reason.slice(-1) === '.') {
|
|
||||||
entry.reason = entry.reason.slice(0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
content += entry.reason + '\n\n';
|
|
||||||
//remove leading and trailing commas and whitespaces
|
//remove leading and trailing commas and whitespaces
|
||||||
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
untranslatedContent = untranslatedContent.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
untranslatedContent = untranslatedContent.substring(0, 1).toUpperCase() + untranslatedContent.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotationWrapper.legalBasis && !isDocumine) {
|
if (annotationWrapper.legalBasis && !isDocumine) {
|
||||||
content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
|
params['hasLb'] = 'true';
|
||||||
|
params['legalBasis'] = annotationWrapper.legalBasis;
|
||||||
|
untranslatedContent += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotationWrapper.hasBeenRemovedByManualOverride) {
|
if (annotationWrapper.hasBeenRemovedByManualOverride) {
|
||||||
content += 'Removed by manual override';
|
params['hasOverride'] = 'true';
|
||||||
|
untranslatedContent += 'Removed by manual override';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.section) {
|
if (logEntry.section) {
|
||||||
|
params['hasSection'] = 'true';
|
||||||
|
params['sectionSymbol'] = isDocumine ? '' : ':';
|
||||||
|
params['shouldLower'] = untranslatedContent.length.toString();
|
||||||
|
params['section'] = logEntry.section;
|
||||||
let prefix = `In section${isDocumine ? '' : ':'} `;
|
let prefix = `In section${isDocumine ? '' : ':'} `;
|
||||||
if (content.length) {
|
if (untranslatedContent.length) {
|
||||||
prefix = ` ${prefix.toLowerCase()}`;
|
prefix = ` ${prefix.toLowerCase()}`;
|
||||||
}
|
}
|
||||||
content += `${prefix} "${entry.section}"`;
|
untranslatedContent += `${prefix} "${logEntry.section}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
|
return { translation: _('annotation-content'), params: params, untranslatedContent: untranslatedContent };
|
||||||
annotationWrapper.content = content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getShortContent(annotationWrapper: AnnotationWrapper) {
|
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
|
||||||
if (annotationWrapper.legalBasis) {
|
if (annotationWrapper.legalBasis) {
|
||||||
const lb = annotationWrapper.legalBasisList.find(
|
const lb = legalBasisList.find(lbm => lbm.technicalName?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
|
||||||
lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()),
|
|
||||||
);
|
|
||||||
if (lb) {
|
if (lb) {
|
||||||
return lb.name;
|
return lb.name;
|
||||||
}
|
}
|
||||||
@ -473,154 +386,4 @@ export class AnnotationWrapper implements IListable {
|
|||||||
|
|
||||||
return annotationWrapper.legalBasis;
|
return annotationWrapper.legalBasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #selectSuperType(redactionLogEntry: IRedactionLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
|
|
||||||
switch (lastManualChange.manualRedactionType) {
|
|
||||||
case ManualRedactionTypes.ADD_LOCALLY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.ManualRedaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.DeclinedSuggestion;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionAdd;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.ADD_TO_DICTIONARY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.DeclinedSuggestion;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionAddDictionary;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.REMOVE_LOCALLY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED: {
|
|
||||||
if (isHintDictionary) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redactionLogEntry.redacted) {
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SuperTypes.Skipped;
|
|
||||||
}
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemove;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
|
|
||||||
if (redactionLogEntry.redacted) {
|
|
||||||
if (lastManualChange.processed) {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (lastManualChange.processed) {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return redactionLogEntry.recommendation ? SuperTypes.Recommendation : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.FORCE_REDACT:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionForceRedaction;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.FORCE_HINT:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.IgnoredHint;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionForceHint;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.RECATEGORIZE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED: {
|
|
||||||
if (redactionLogEntry.recommendation) {
|
|
||||||
return SuperTypes.Recommendation;
|
|
||||||
} else if (redactionLogEntry.redacted) {
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
} else if (redactionLogEntry.hint) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
}
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRecategorizeImage;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.LEGAL_BASIS_CHANGE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionChangeLegalBasis;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.RESIZE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
if (redactionLogEntry.recommendation) {
|
|
||||||
return SuperTypes.Recommendation;
|
|
||||||
} else if (redactionLogEntry.redacted) {
|
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction
|
|
||||||
? SuperTypes.ManualRedaction
|
|
||||||
: SuperTypes.Redaction;
|
|
||||||
} else if (redactionLogEntry.hint) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionResize;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,10 @@ import { IqserPermissionsService } from '@iqser/common-ui';
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
|
import { SideNavComponent } from '@common-ui/shared';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
@ -17,6 +21,7 @@ interface NavItem {
|
|||||||
templateUrl: './account-side-nav.component.html',
|
templateUrl: './account-side-nav.component.html',
|
||||||
styleUrls: ['./account-side-nav.component.scss'],
|
styleUrls: ['./account-side-nav.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [SideNavComponent, TranslateModule, NgForOf, NgIf, RouterLinkActive, RouterLink],
|
||||||
})
|
})
|
||||||
export class AccountSideNavComponent {
|
export class AccountSideNavComponent {
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SharedModule } from '@shared/shared.module';
|
|
||||||
import { AccountRoutingModule } from './account-routing.module';
|
|
||||||
import { AccountSideNavComponent } from './account-side-nav/account-side-nav.component';
|
|
||||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
|
||||||
import { NotificationPreferencesService } from './services/notification-preferences.service';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { IconButtonComponent, IqserAllowDirective, IqserHelpModeModule } from '@iqser/common-ui';
|
|
||||||
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
|
||||||
import { SideNavComponent } from '@iqser/common-ui/lib/shared';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [AccountSideNavComponent, BaseAccountScreenComponent, PreferencesComponent],
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
SharedModule,
|
|
||||||
AccountRoutingModule,
|
|
||||||
TranslateModule,
|
|
||||||
IqserHelpModeModule,
|
|
||||||
IconButtonComponent,
|
|
||||||
SideNavComponent,
|
|
||||||
IqserAllowDirective,
|
|
||||||
],
|
|
||||||
providers: [NotificationPreferencesService],
|
|
||||||
})
|
|
||||||
export class AccountModule {}
|
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||||
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { NotificationPreferencesService } from './services/notification-preferences.service';
|
||||||
|
|
||||||
const routes: IqserRoutes = [
|
export default [
|
||||||
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
|
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
|
||||||
{
|
{
|
||||||
path: 'user-profile',
|
path: 'user-profile',
|
||||||
@ -16,12 +16,13 @@ const routes: IqserRoutes = [
|
|||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./screens/user-profile/user-profile.module').then(m => m.UserProfileModule),
|
loadChildren: () => import('./screens/user-profile/user-profile.routes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'notifications',
|
path: 'notifications',
|
||||||
component: BaseAccountScreenComponent,
|
component: BaseAccountScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
permissions: {
|
permissions: {
|
||||||
@ -29,7 +30,8 @@ const routes: IqserRoutes = [
|
|||||||
redirectTo: '/',
|
redirectTo: '/',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./screens/notifications/notifications.module').then(m => m.NotificationsModule),
|
providers: [NotificationPreferencesService],
|
||||||
|
loadChildren: () => import('./screens/notifications/notifications.routes'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preferences',
|
path: 'preferences',
|
||||||
@ -61,10 +63,4 @@ const routes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
] satisfies IqserRoutes;
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forChild(routes)],
|
|
||||||
exports: [RouterModule],
|
|
||||||
})
|
|
||||||
export class AccountRoutingModule {}
|
|
||||||
@ -7,11 +7,12 @@
|
|||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div class="content-container full-height">
|
<div class="content-container full-height">
|
||||||
<div class="overlay-shadow"></div>
|
<div class="overlay-shadow"></div>
|
||||||
<div class="dialog">
|
<div [ngClass]="!isWarningsScreen && 'dialog'">
|
||||||
<div class="dialog-header">
|
@if (!isWarningsScreen) {
|
||||||
<div class="heading-l" [translate]="translations[path]"></div>
|
<div class="dialog-header">
|
||||||
</div>
|
<div class="heading-l" [translate]="translations[path]"></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,26 +1,35 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router, RouterOutlet } from '@angular/router';
|
||||||
import { accountTranslations } from '@translations/account-translations';
|
import { accountTranslations } from '@translations/account-translations';
|
||||||
|
import { NgClass } from '@angular/common';
|
||||||
|
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-base-account-screen',
|
selector: 'redaction-base-account-screen',
|
||||||
templateUrl: './base-account-screen-component.html',
|
templateUrl: './base-account-screen-component.html',
|
||||||
styleUrls: ['./base-account-screen-component.scss'],
|
styleUrls: ['./base-account-screen-component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
||||||
})
|
})
|
||||||
export class BaseAccountScreenComponent implements OnInit {
|
export class BaseAccountScreenComponent implements OnInit {
|
||||||
readonly translations = accountTranslations;
|
readonly translations = accountTranslations;
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
|
readonly isWarningsScreen: boolean;
|
||||||
|
|
||||||
constructor(private readonly _router: Router, private readonly _hostRef: ViewContainerRef) {
|
constructor(
|
||||||
|
private readonly _router: Router,
|
||||||
|
private readonly _hostRef: ViewContainerRef,
|
||||||
|
) {
|
||||||
this.path = this._router.url.split('/').pop();
|
this.path = this._router.url.split('/').pop();
|
||||||
|
this.isWarningsScreen = this.path === 'warnings-preferences';
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this._setDialogWidth();
|
this.#setDialogWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setDialogWidth() {
|
#setDialogWidth() {
|
||||||
const element = this._hostRef.element.nativeElement as HTMLElement;
|
const element = this._hostRef.element.nativeElement as HTMLElement;
|
||||||
element.style.setProperty('--width', this.path === 'user-profile' ? 'unset' : '100%');
|
element.style.setProperty('--width', this.path === 'user-profile' ? 'unset' : '100%');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,12 @@
|
|||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div *ngFor="let category of notificationCategories">
|
<div *ngFor="let category of notificationCategories">
|
||||||
<div class="iqser-input-group header w-full">
|
<div class="iqser-input-group header w-full">
|
||||||
<mat-slide-toggle color="primary" formControlName="{{ category }}Enabled">{{
|
<mat-slide-toggle color="primary" formControlName="{{ category }}Enabled"
|
||||||
translations[category] | translate
|
>{{ translations[category] | translate }}
|
||||||
}}</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- TODO: This lots of getters-->
|
||||||
<div *ngIf="isCategoryActive(category)" class="options-content">
|
<div *ngIf="isCategoryActive(category)" class="options-content">
|
||||||
<div [translate]="'notifications-screen.options-title'" class="statement"></div>
|
<div [translate]="'notifications-screen.options-title'" class="statement"></div>
|
||||||
|
|
||||||
@ -15,8 +16,8 @@
|
|||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<ng-container *ngFor="let preference of getRssFilteredSettings(notificationGroupsValues[i])">
|
<ng-container *ngFor="let preference of getRssFilteredSettings(notificationGroupsValues[i])">
|
||||||
<mat-checkbox
|
<mat-checkbox
|
||||||
*ngIf="!skipPreference(preference)"
|
|
||||||
(change)="addRemovePreference($event.checked, category, preference)"
|
(change)="addRemovePreference($event.checked, category, preference)"
|
||||||
|
*ngIf="!skipPreference(preference)"
|
||||||
[checked]="isPreferenceChecked(category, preference)"
|
[checked]="isPreferenceChecked(category, preference)"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
||||||
import { UntypedFormBuilder } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
|
||||||
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
||||||
import { BaseFormComponent, getConfig, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, getConfig, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
DocumentNotificationsTypes,
|
DocumentNotificationsTypes,
|
||||||
@ -14,6 +14,10 @@ import {
|
|||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
|
||||||
const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_REVIEWER', 'ASSIGN_REVIEWER'];
|
const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_REVIEWER', 'ASSIGN_REVIEWER'];
|
||||||
|
|
||||||
@ -21,6 +25,7 @@ const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_RE
|
|||||||
templateUrl: './notifications-screen.component.html',
|
templateUrl: './notifications-screen.component.html',
|
||||||
styleUrls: ['./notifications-screen.component.scss'],
|
styleUrls: ['./notifications-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [ReactiveFormsModule, NgForOf, MatSlideToggle, TranslateModule, NgIf, MatCheckbox, IconButtonComponent],
|
||||||
})
|
})
|
||||||
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly #toaster = inject(Toaster);
|
readonly #toaster = inject(Toaster);
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SharedModule } from '@shared/shared.module';
|
|
||||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { IconButtonComponent } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [NotificationsScreenComponent],
|
|
||||||
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent],
|
|
||||||
})
|
|
||||||
export class NotificationsModule {}
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { IqserRoutes } from '@iqser/common-ui';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: NotificationsScreenComponent,
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
},
|
||||||
|
] satisfies IqserRoutes;
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
<div class="dialog">
|
||||||
|
<form [formGroup]="form">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<h1>{{ 'dialog-defaults-form.title' | translate }}</h1>
|
||||||
|
<h3>{{ 'dialog-defaults-form.redaction.title' | translate }}</h3>
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label translate="dialog-defaults-form.redaction.add-dialog"></label>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="addRedaction">
|
||||||
|
<mat-option *ngFor="let option of redactionAddOptions" [value]="option.value">{{
|
||||||
|
option.label | translate
|
||||||
|
}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox *ngIf="displayExtraOptionAddRedaction" formControlName="addRedactionApplyToAll">{{
|
||||||
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
|
}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label translate="dialog-defaults-form.redaction.remove-dialog"></label>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="removeRedaction">
|
||||||
|
<mat-option *ngFor="let option of redactionRemoveOptions" [value]="option.value">{{
|
||||||
|
option.label | translate
|
||||||
|
}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox *ngIf="displayExtraOptionRemoveRedaction" formControlName="removeRedactionApplyToAll">{{
|
||||||
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
|
}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<h3>{{ 'dialog-defaults-form.recommendation.title' | translate }}</h3>
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label translate="dialog-defaults-form.recommendation.remove-dialog"></label>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="removeRecommendation">
|
||||||
|
<mat-option *ngFor="let option of recommendationRemoveOptions" [value]="option.value">{{
|
||||||
|
option.label | translate
|
||||||
|
}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation" formControlName="removeRecommendationApplyToAll">{{
|
||||||
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
|
}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<h3>{{ 'dialog-defaults-form.hint.title' | translate }}</h3>
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label translate="dialog-defaults-form.hint.add-dialog"></label>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="addHint">
|
||||||
|
<mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox *ngIf="displayExtraOptionAddHint" formControlName="addHintApplyToAll">{{
|
||||||
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
|
}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label translate="dialog-defaults-form.hint.remove-dialog"></label>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-select formControlName="removeHint">
|
||||||
|
<mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox *ngIf="displayExtraOptionRemoveHint" formControlName="removeHintApplyToAll">{{
|
||||||
|
'dialog-defaults-form.extra-option-label' | translate
|
||||||
|
}}</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<iqser-icon-button
|
||||||
|
(action)="save()"
|
||||||
|
[disabled]="!valid || !changed"
|
||||||
|
[label]="'preferences-screen.actions.save' | translate"
|
||||||
|
[type]="iconButtonTypes.primary"
|
||||||
|
></iqser-icon-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
mat-checkbox {
|
||||||
|
margin: 8px 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .iqser-input-group:not(first-of-type) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
|
import { ChangeDetectorRef, Component, inject } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
|
import { BaseFormComponent } from '@common-ui/form';
|
||||||
|
import { AsControl } from '@common-ui/utils';
|
||||||
|
import { IconButtonComponent } from '@iqser/common-ui';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
import {
|
||||||
|
RedactOrHintOption,
|
||||||
|
RedactOrHintOptions,
|
||||||
|
RemoveRedactionOption,
|
||||||
|
RemoveRedactionOptions,
|
||||||
|
} from '../../../../file-preview/utils/dialog-types';
|
||||||
|
import {
|
||||||
|
hintAddOptions,
|
||||||
|
recommendationRemoveOptions,
|
||||||
|
redactionAddOptions,
|
||||||
|
redactionRemoveOptions,
|
||||||
|
removeOptions,
|
||||||
|
SystemDefaultType,
|
||||||
|
} from '../../../utils/dialog-defaults';
|
||||||
|
|
||||||
|
interface DefaultOptionsForm {
|
||||||
|
addRedaction: RedactOrHintOption | SystemDefaultType;
|
||||||
|
addHint: RedactOrHintOption | SystemDefaultType;
|
||||||
|
removeRedaction: RemoveRedactionOption | SystemDefaultType;
|
||||||
|
removeRecommendation: RemoveRedactionOption | SystemDefaultType;
|
||||||
|
removeHint: RemoveRedactionOption | SystemDefaultType;
|
||||||
|
addRedactionApplyToAll: boolean;
|
||||||
|
removeRedactionApplyToAll: boolean;
|
||||||
|
removeRecommendationApplyToAll: boolean;
|
||||||
|
addHintApplyToAll: boolean;
|
||||||
|
removeHintApplyToAll: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-dialog-defaults',
|
||||||
|
templateUrl: './dialog-defaults.component.html',
|
||||||
|
styleUrl: './dialog-defaults.component.scss',
|
||||||
|
imports: [ReactiveFormsModule, TranslateModule, MatFormField, MatSelect, MatOption, NgForOf, MatCheckbox, NgIf, IconButtonComponent],
|
||||||
|
})
|
||||||
|
export class DialogDefaultsComponent extends BaseFormComponent {
|
||||||
|
readonly #formBuilder = inject(FormBuilder);
|
||||||
|
readonly #userPreferences = inject(UserPreferenceService);
|
||||||
|
readonly #changeDetectorRef = inject(ChangeDetectorRef);
|
||||||
|
form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group({
|
||||||
|
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
||||||
|
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
||||||
|
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
||||||
|
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
|
||||||
|
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
|
||||||
|
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
|
||||||
|
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
|
||||||
|
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
||||||
|
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
||||||
|
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
||||||
|
});
|
||||||
|
initialFormValue = this.form.getRawValue();
|
||||||
|
|
||||||
|
readonly redactionAddOptions = redactionAddOptions;
|
||||||
|
readonly hintAddOptions = hintAddOptions;
|
||||||
|
readonly removeOptions = removeOptions;
|
||||||
|
readonly redactionRemoveOptions = redactionRemoveOptions;
|
||||||
|
readonly recommendationRemoveOptions = recommendationRemoveOptions;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayExtraOptionAddRedaction() {
|
||||||
|
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addRedaction.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayExtraOptionAddHint() {
|
||||||
|
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addHint.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayExtraOptionRemoveRedaction() {
|
||||||
|
return (
|
||||||
|
[RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial<
|
||||||
|
RemoveRedactionOption | SystemDefaultType
|
||||||
|
>[]
|
||||||
|
).includes(this.form.controls.removeRedaction.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayExtraOptionRemoveHint() {
|
||||||
|
return RemoveRedactionOptions.IN_DOSSIER === this.form.controls.removeHint.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayExtraOptionRemoveRecommendation() {
|
||||||
|
return RemoveRedactionOptions.DO_NOT_RECOMMEND === this.form.controls.removeRecommendation.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(): Promise<any> {
|
||||||
|
const formValue = this.form.value;
|
||||||
|
|
||||||
|
if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) {
|
||||||
|
await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value);
|
||||||
|
}
|
||||||
|
if (this.initialFormValue.addHint !== this.form.controls.addHint.value) {
|
||||||
|
await this.#userPreferences.saveAddHintDefaultOption(this.form.controls.addHint.value);
|
||||||
|
}
|
||||||
|
if (this.initialFormValue.removeRedaction !== this.form.controls.removeRedaction.value) {
|
||||||
|
await this.#userPreferences.saveRemoveRedactionDefaultOption(this.form.controls.removeRedaction.value);
|
||||||
|
}
|
||||||
|
if (this.initialFormValue.removeRecommendation !== this.form.controls.removeRecommendation.value) {
|
||||||
|
await this.#userPreferences.saveRemoveRecommendationDefaultOption(this.form.controls.removeRecommendation.value);
|
||||||
|
}
|
||||||
|
if (this.initialFormValue.removeHint !== this.form.controls.removeHint.value) {
|
||||||
|
await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayExtraOptionAddRedaction) {
|
||||||
|
if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) {
|
||||||
|
await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayExtraOptionAddHint) {
|
||||||
|
if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) {
|
||||||
|
await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.#userPreferences.saveAddHintDefaultExtraOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayExtraOptionRemoveRedaction) {
|
||||||
|
if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) {
|
||||||
|
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayExtraOptionRemoveHint) {
|
||||||
|
if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) {
|
||||||
|
await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayExtraOptionRemoveRecommendation) {
|
||||||
|
if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) {
|
||||||
|
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption(
|
||||||
|
this.form.controls.removeRecommendationApplyToAll.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.#userPreferences.reload();
|
||||||
|
this.#patchValues();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
this.#changeDetectorRef.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
#patchValues() {
|
||||||
|
this.form.patchValue({
|
||||||
|
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
||||||
|
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
||||||
|
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
||||||
|
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
|
||||||
|
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
|
||||||
|
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
|
||||||
|
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
|
||||||
|
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
||||||
|
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
||||||
|
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,45 +1,52 @@
|
|||||||
<form [formGroup]="form">
|
<redaction-dialog-defaults *ngIf="currentScreen === screens.WARNING_PREFERENCES && !config.IS_DOCUMINE"></redaction-dialog-defaults>
|
||||||
<div class="dialog-content">
|
|
||||||
<div *ngIf="currentScreen === screens.WARNING_PREFERENCES" class="content-delimiter"></div>
|
|
||||||
<div class="dialog-content-left">
|
|
||||||
<ng-container *ngIf="currentScreen === screens.PREFERENCES">
|
|
||||||
<div class="iqser-input-group">
|
|
||||||
<mat-slide-toggle color="primary" formControlName="autoExpandFiltersOnActions">
|
|
||||||
{{ 'preferences-screen.form.auto-expand-filters-on-action' | translate }}
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="config.IS_DOCUMINE" class="iqser-input-group">
|
<div [ngClass]="currentScreen === screens.WARNING_PREFERENCES && 'dialog'">
|
||||||
<mat-slide-toggle color="primary" formControlName="openScmDialogByDefault">
|
<form [formGroup]="form">
|
||||||
{{ 'preferences-screen.form.open-structured-view-by-default' | translate }}
|
<div class="dialog-content">
|
||||||
</mat-slide-toggle>
|
<div class="dialog-content-left">
|
||||||
</div>
|
<ng-container *ngIf="currentScreen === screens.PREFERENCES">
|
||||||
|
<div class="iqser-input-group">
|
||||||
|
<mat-slide-toggle color="primary" formControlName="autoExpandFiltersOnActions">
|
||||||
|
{{ 'preferences-screen.form.auto-expand-filters-on-action' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *allow="roles.getTables" class="iqser-input-group">
|
<div *allow="roles.getTables" class="iqser-input-group">
|
||||||
<label [translate]="'preferences-screen.form.table-extraction-type'"></label>
|
<label [translate]="'preferences-screen.form.table-extraction-type'"></label>
|
||||||
<input formControlName="tableExtractionType" />
|
<input formControlName="tableExtractionType" />
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="currentScreen === screens.WARNING_PREFERENCES">
|
<ng-container *ngIf="currentScreen === screens.WARNING_PREFERENCES">
|
||||||
<p class="warnings-subtitle">{{ 'preferences-screen.warnings-subtitle' | translate }}</p>
|
<h1>{{ 'preferences-screen.warnings-subtitle' | translate }}</h1>
|
||||||
<p class="warnings-description">{{ 'preferences-screen.warnings-description' | translate }}</p>
|
<p class="warnings-description">{{ 'preferences-screen.warnings-description' | translate }}</p>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="loadAllAnnotationsWarning">
|
<mat-checkbox color="primary" formControlName="loadAllAnnotationsWarning">
|
||||||
{{ 'preferences-screen.form.load-all-annotations-warning' | translate }}
|
{{ 'preferences-screen.form.load-all-annotations-warning' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
<div class="iqser-input-group">
|
||||||
|
<mat-checkbox color="primary" formControlName="helpModeDialog">
|
||||||
|
{{ 'preferences-screen.form.help-mode-dialog' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="iqser-input-group">
|
||||||
|
<mat-checkbox color="primary" formControlName="overwriteFileOption">
|
||||||
|
{{ 'preferences-screen.form.overwrite-file-option' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[disabled]="!valid || !changed"
|
[disabled]="!valid || !changed"
|
||||||
[label]="'preferences-screen.actions.save' | translate"
|
[label]="'preferences-screen.actions.save' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,3 @@
|
|||||||
@use 'variables';
|
|
||||||
|
|
||||||
.content-delimiter {
|
|
||||||
border-top: 1px solid var(--iqser-separator);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warnings-subtitle {
|
|
||||||
font-size: var(--iqser-font-size);
|
|
||||||
color: var(--iqser-text);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warnings-description {
|
.warnings-description {
|
||||||
width: 105%;
|
width: 105%;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,33 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { BaseFormComponent, getConfig, IqserPermissionsService, isIqserDevMode, LoadingService } from '@iqser/common-ui';
|
import {
|
||||||
|
BaseFormComponent,
|
||||||
|
getConfig,
|
||||||
|
IconButtonComponent,
|
||||||
|
IqserAllowDirective,
|
||||||
|
IqserPermissionsService,
|
||||||
|
isIqserDevMode,
|
||||||
|
KEYS,
|
||||||
|
LoadingService,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
import { AsControl } from '@iqser/common-ui/lib/utils';
|
import { AsControl } from '@iqser/common-ui/lib/utils';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
import { DialogDefaultsComponent } from './dialog-defaults/dialog-defaults.component';
|
||||||
|
import { NgClass, NgIf } from '@angular/common';
|
||||||
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
|
||||||
interface PreferencesForm {
|
interface PreferencesForm {
|
||||||
// preferences
|
// preferences
|
||||||
autoExpandFiltersOnActions: boolean;
|
autoExpandFiltersOnActions: boolean;
|
||||||
openScmDialogByDefault: boolean;
|
|
||||||
tableExtractionType: string;
|
tableExtractionType: string;
|
||||||
// warnings preferences
|
// warnings preferences
|
||||||
loadAllAnnotationsWarning: boolean;
|
loadAllAnnotationsWarning: boolean;
|
||||||
|
helpModeDialog: boolean;
|
||||||
|
overwriteFileOption: boolean;
|
||||||
|
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
}
|
}
|
||||||
@ -29,8 +44,25 @@ const Screens = {
|
|||||||
templateUrl: './preferences.component.html',
|
templateUrl: './preferences.component.html',
|
||||||
styleUrls: ['./preferences.component.scss'],
|
styleUrls: ['./preferences.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [
|
||||||
|
DialogDefaultsComponent,
|
||||||
|
NgClass,
|
||||||
|
NgIf,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatSlideToggle,
|
||||||
|
TranslateModule,
|
||||||
|
IqserAllowDirective,
|
||||||
|
MatCheckbox,
|
||||||
|
IconButtonComponent,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
||||||
|
readonly #formBuilder = inject(FormBuilder);
|
||||||
|
readonly #permissionsService = inject(IqserPermissionsService);
|
||||||
|
readonly #changeRef = inject(ChangeDetectorRef);
|
||||||
|
readonly #loadingService = inject(LoadingService);
|
||||||
|
readonly #userPreferenceService = inject(UserPreferenceService);
|
||||||
|
|
||||||
readonly form: FormGroup<AsControl<PreferencesForm>>;
|
readonly form: FormGroup<AsControl<PreferencesForm>>;
|
||||||
readonly currentScreen: Screen;
|
readonly currentScreen: Screen;
|
||||||
readonly screens = Screens;
|
readonly screens = Screens;
|
||||||
@ -39,75 +71,87 @@ export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
|||||||
readonly config = getConfig();
|
readonly config = getConfig();
|
||||||
readonly isIqserDevMode = isIqserDevMode();
|
readonly isIqserDevMode = isIqserDevMode();
|
||||||
|
|
||||||
constructor(
|
get #isOverwriteFileOptionActive() {
|
||||||
route: ActivatedRoute,
|
return !(this.#userPreferenceService.getOverwriteFileOption() === 'undefined');
|
||||||
readonly userPreferenceService: UserPreferenceService,
|
}
|
||||||
private readonly _formBuilder: FormBuilder,
|
|
||||||
private readonly _permissionsService: IqserPermissionsService,
|
constructor(route: ActivatedRoute) {
|
||||||
private readonly _changeRef: ChangeDetectorRef,
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.form = this._formBuilder.group({
|
this.form = this.#formBuilder.group({
|
||||||
// preferences
|
// preferences
|
||||||
autoExpandFiltersOnActions: [this.userPreferenceService.getAutoExpandFiltersOnActions()],
|
autoExpandFiltersOnActions: [this.#userPreferenceService.getAutoExpandFiltersOnActions()],
|
||||||
openScmDialogByDefault: [this.userPreferenceService.getOpenScmDialogByDefault()],
|
tableExtractionType: [this.#userPreferenceService.getTableExtractionType()],
|
||||||
tableExtractionType: [this.userPreferenceService.getTableExtractionType()],
|
|
||||||
// warnings preferences
|
// warnings preferences
|
||||||
loadAllAnnotationsWarning: [this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
|
loadAllAnnotationsWarning: [this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
|
||||||
|
helpModeDialog: [this.#userPreferenceService.getBool(KEYS.helpModeDialog)],
|
||||||
|
overwriteFileOption: [this.#isOverwriteFileOptionActive],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this._permissionsService.has(Roles.managePreferences)) {
|
if (!this.#permissionsService.has(Roles.managePreferences)) {
|
||||||
this.form.disable();
|
this.form.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._permissionsService.has(Roles.getTables)) {
|
if (!this.#permissionsService.has(Roles.getTables)) {
|
||||||
this.form.controls.tableExtractionType.disable();
|
this.form.controls.tableExtractionType.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.#isOverwriteFileOptionActive) {
|
||||||
|
this.form.controls.overwriteFileOption.disable();
|
||||||
|
}
|
||||||
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this.currentScreen = route.snapshot.data.screen;
|
this.currentScreen = route.snapshot.data.screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._loadingService.stop();
|
this.#loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<any> {
|
async save(): Promise<any> {
|
||||||
if (this.form.controls.autoExpandFiltersOnActions.value !== this.userPreferenceService.getAutoExpandFiltersOnActions()) {
|
if (this.form.controls.autoExpandFiltersOnActions.value !== this.#userPreferenceService.getAutoExpandFiltersOnActions()) {
|
||||||
await this.userPreferenceService.toggleAutoExpandFiltersOnActions();
|
await this.#userPreferenceService.toggleAutoExpandFiltersOnActions();
|
||||||
}
|
|
||||||
if (this.form.controls.openScmDialogByDefault.value !== this.userPreferenceService.getOpenScmDialogByDefault()) {
|
|
||||||
await this.userPreferenceService.toggleOpenScmDialogByDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.form.controls.tableExtractionType.value !== this.userPreferenceService.getTableExtractionType()) {
|
if (this.form.controls.tableExtractionType.value !== this.#userPreferenceService.getTableExtractionType()) {
|
||||||
await this.userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
|
await this.#userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.form.controls.loadAllAnnotationsWarning.value !==
|
this.form.controls.loadAllAnnotationsWarning.value !==
|
||||||
this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
|
this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
|
||||||
) {
|
) {
|
||||||
await this.userPreferenceService.save(
|
await this.#userPreferenceService.save(
|
||||||
PreferencesKeys.loadAllAnnotationsWarning,
|
PreferencesKeys.loadAllAnnotationsWarning,
|
||||||
String(this.form.controls.loadAllAnnotationsWarning.value),
|
String(this.form.controls.loadAllAnnotationsWarning.value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userPreferenceService.reload();
|
if (this.form.controls.helpModeDialog.value !== this.#userPreferenceService.getBool(KEYS.helpModeDialog)) {
|
||||||
|
await this.#userPreferenceService.save(KEYS.helpModeDialog, String(this.form.controls.helpModeDialog.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.form.controls.overwriteFileOption.enabled && !this.form.controls.overwriteFileOption.value) {
|
||||||
|
await this.#userPreferenceService.saveOverwriteFileOption('undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.#userPreferenceService.reload();
|
||||||
this.#patchValues();
|
this.#patchValues();
|
||||||
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this._changeRef.markForCheck();
|
this.#changeRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
#patchValues() {
|
#patchValues() {
|
||||||
this.form.patchValue({
|
this.form.patchValue({
|
||||||
autoExpandFiltersOnActions: this.userPreferenceService.getAutoExpandFiltersOnActions(),
|
autoExpandFiltersOnActions: this.#userPreferenceService.getAutoExpandFiltersOnActions(),
|
||||||
openScmDialogByDefault: this.userPreferenceService.getOpenScmDialogByDefault(),
|
tableExtractionType: this.#userPreferenceService.getTableExtractionType(),
|
||||||
tableExtractionType: this.userPreferenceService.getTableExtractionType(),
|
loadAllAnnotationsWarning: this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
|
||||||
loadAllAnnotationsWarning: this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
|
helpModeDialog: this.#userPreferenceService.getBool(KEYS.helpModeDialog),
|
||||||
|
overwriteFileOption: this.#isOverwriteFileOptionActive,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!this.#isOverwriteFileOptionActive) {
|
||||||
|
this.form.controls.overwriteFileOption.disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { AbstractControl, FormGroup, Validators } from '@angular/forms';
|
import { AbstractControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
interface FormType {
|
interface FormType {
|
||||||
password: AbstractControl<string>;
|
password: AbstractControl<string>;
|
||||||
@ -9,6 +10,7 @@ interface FormType {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './confirm-password-dialog.component.html',
|
templateUrl: './confirm-password-dialog.component.html',
|
||||||
|
imports: [ReactiveFormsModule, IconButtonComponent, TranslateModule, CircleButtonComponent],
|
||||||
})
|
})
|
||||||
export class ConfirmPasswordDialogComponent extends BaseDialogComponent {
|
export class ConfirmPasswordDialogComponent extends BaseDialogComponent {
|
||||||
constructor(protected readonly _dialogRef: MatDialogRef<ConfirmPasswordDialogComponent>) {
|
constructor(protected readonly _dialogRef: MatDialogRef<ConfirmPasswordDialogComponent>) {
|
||||||
|
|||||||
@ -16,32 +16,37 @@
|
|||||||
<input formControlName="lastName" name="lastName" type="text" />
|
<input formControlName="lastName" name="lastName" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="devMode" class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
|
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="language">
|
<mat-select formControlName="language">
|
||||||
<mat-option *ngFor="let language of languages" [value]="language">
|
<mat-select-trigger>{{ languageSelectLabel() | translate }}</mat-select-trigger>
|
||||||
{{ translations[language] | translate }}
|
@for (language of languages; track language) {
|
||||||
</mat-option>
|
<mat-option [value]="language">
|
||||||
|
{{ translations[language] | translate }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<a [href]="changePasswordUrl" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a>
|
<a (click)="resetPassword()" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="devMode" class="iqser-input-group">
|
@if (devMode) {
|
||||||
<mat-slide-toggle color="primary" formControlName="darkTheme">
|
<div class="iqser-input-group">
|
||||||
{{ 'user-profile-screen.form.dark-theme' | translate }}
|
<mat-slide-toggle color="primary" formControlName="darkTheme">
|
||||||
</mat-slide-toggle>
|
{{ 'user-profile-screen.form.dark-theme' | translate }}
|
||||||
</div>
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
[disabled]="form.invalid || !(profileChanged || languageChanged || themeChanged)"
|
[disabled]="disabled"
|
||||||
[label]="'user-profile-screen.actions.save' | translate"
|
[label]="'user-profile-screen.actions.save' | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
|
|||||||
@ -1,31 +1,64 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { BaseFormComponent, getConfig, IqserPermissionsService, LanguageService, LoadingService, Toaster } from '@iqser/common-ui';
|
import {
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
BaseFormComponent,
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
IconButtonComponent,
|
||||||
import { AppConfig, IProfile } from '@red/domain';
|
IqserPermissionsService,
|
||||||
|
LanguageService,
|
||||||
|
LoadingService,
|
||||||
|
Toaster,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
|
import { IProfile } from '@red/domain';
|
||||||
import { languagesTranslations } from '@translations/languages-translations';
|
import { languagesTranslations } from '@translations/languages-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
|
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
|
||||||
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
|
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
||||||
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
|
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
|
||||||
|
import { formControlToSignal } from '@utils/functions';
|
||||||
|
import { AsControl } from '@common-ui/utils';
|
||||||
|
|
||||||
|
interface UserProfileForm {
|
||||||
|
email: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
language: string;
|
||||||
|
darkTheme: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './user-profile-screen.component.html',
|
templateUrl: './user-profile-screen.component.html',
|
||||||
styleUrls: ['./user-profile-screen.component.scss'],
|
styleUrls: ['./user-profile-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatFormField,
|
||||||
|
MatSelect,
|
||||||
|
MatOption,
|
||||||
|
TranslateModule,
|
||||||
|
MatSlideToggle,
|
||||||
|
IconButtonComponent,
|
||||||
|
MatSelectTrigger,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
export class UserProfileScreenComponent extends BaseFormComponent {
|
||||||
#profileModel: IProfile;
|
readonly form: FormGroup<AsControl<UserProfileForm>> = this.#getForm();
|
||||||
|
initialFormValue = this.form.getRawValue();
|
||||||
readonly translations = languagesTranslations;
|
readonly translations = languagesTranslations;
|
||||||
readonly devMode = this._userPreferenceService.isIqserDevMode;
|
readonly devMode = this._userPreferenceService.isIqserDevMode;
|
||||||
readonly changePasswordUrl: string;
|
|
||||||
|
readonly profileKeys = ['email', 'firstName', 'lastName'];
|
||||||
|
readonly languages = this._translateService.langs;
|
||||||
|
readonly language = formControlToSignal<UserProfileForm['language']>(this.form.controls.language);
|
||||||
|
readonly languageSelectLabel = computed(() => this.translations[this.language()]);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
domSanitizer: DomSanitizer,
|
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _dialogService: UserProfileDialogService,
|
private readonly _dialogService: UserProfileDialogService,
|
||||||
@ -36,46 +69,29 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
|
|||||||
protected readonly _userPreferenceService: UserPreferenceService,
|
protected readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _changeRef: ChangeDetectorRef,
|
private readonly _changeRef: ChangeDetectorRef,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
|
private readonly _pdfViewer: PdfViewer,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._loadingService.start();
|
if (!this._permissionsService.has(Roles.updateMyProfile)) {
|
||||||
const tenant = inject(TenantsService).activeTenantId;
|
this.form.disable();
|
||||||
const realmUrl = `${getConfig<AppConfig>().OAUTH_URL}/realms/${tenant}`;
|
}
|
||||||
this.changePasswordUrl = `${realmUrl}/account/password`;
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
get languageChanged(): boolean {
|
get languageChanged(): boolean {
|
||||||
return this.#profileModel['language'] !== this.form.get('language').value;
|
return this.initialFormValue['language'] !== this.form.controls.language.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get themeChanged(): boolean {
|
get themeChanged(): boolean {
|
||||||
return this.#profileModel['darkTheme'] !== this.form.get('darkTheme').value;
|
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get emailChanged(): boolean {
|
get emailChanged(): boolean {
|
||||||
return this.#profileModel['email'] !== this.form.get('email').value;
|
return this.initialFormValue['email'] !== this.form.controls.email.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get profileChanged(): boolean {
|
get profileChanged(): boolean {
|
||||||
const keys = Object.keys(this.form.getRawValue());
|
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
|
||||||
keys.splice(keys.indexOf('language'), 1);
|
|
||||||
keys.splice(keys.indexOf('darkTheme'), 1);
|
|
||||||
|
|
||||||
for (const key of keys) {
|
|
||||||
if (this.#profileModel[key] !== this.form.get(key).value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get languages(): string[] {
|
|
||||||
return this._translateService.langs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this._initializeForm();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
@ -100,50 +116,34 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.languageChanged) {
|
if (this.languageChanged) {
|
||||||
await this._languageService.change(this.form.get('language').value);
|
await this._languageService.change(this.form.controls.language.value);
|
||||||
|
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.themeChanged) {
|
if (this.themeChanged) {
|
||||||
await this._userPreferenceService.saveTheme(this.form.get('darkTheme').value ? 'dark' : 'light');
|
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
|
||||||
}
|
}
|
||||||
|
|
||||||
this._initializeForm();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
this._changeRef.markForCheck();
|
||||||
|
this._loadingService.stop();
|
||||||
this._toaster.success(_('user-profile-screen.update.success'));
|
this._toaster.success(_('user-profile-screen.update.success'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): UntypedFormGroup {
|
async resetPassword() {
|
||||||
return this._formBuilder.group({
|
await this._userService.createResetPasswordAction();
|
||||||
email: ['', [Validators.required, Validators.email]],
|
|
||||||
firstName: [''],
|
|
||||||
lastName: [''],
|
|
||||||
language: [''],
|
|
||||||
darkTheme: [false],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _initializeForm(): void {
|
#getForm() {
|
||||||
try {
|
return this._formBuilder.group({
|
||||||
this.form = this._getForm();
|
email: [this._userService.currentUser.email ?? '', [Validators.required, Validators.email]],
|
||||||
if (!this._permissionsService.has(Roles.updateMyProfile)) {
|
firstName: [this._userService.currentUser.firstName ?? ''],
|
||||||
this.form.disable();
|
lastName: [this._userService.currentUser.lastName ?? ''],
|
||||||
}
|
language: [this._userPreferenceService.getLanguage()],
|
||||||
this.#profileModel = {
|
darkTheme: [this._userPreferenceService.getTheme() === 'dark'],
|
||||||
email: this._userService.currentUser.email ?? '',
|
});
|
||||||
firstName: this._userService.currentUser.firstName ?? '',
|
|
||||||
lastName: this._userService.currentUser.lastName ?? '',
|
|
||||||
language: this._languageService.currentLanguage ?? '',
|
|
||||||
darkTheme: this._userPreferenceService.getTheme() === 'dark',
|
|
||||||
};
|
|
||||||
this.form.patchValue(this.#profileModel, { emitEvent: false });
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
} catch (e) {
|
|
||||||
} finally {
|
|
||||||
this._loadingService.stop();
|
|
||||||
this._changeRef.markForCheck();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { SharedModule } from '@shared/shared.module';
|
|
||||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ConfirmPasswordDialogComponent } from './confirm-password-dialog/confirm-password-dialog.component';
|
|
||||||
import { UserProfileDialogService } from './services/user-profile-dialog.service';
|
|
||||||
import { CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [UserProfileScreenComponent, ConfirmPasswordDialogComponent],
|
|
||||||
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent, CircleButtonComponent],
|
|
||||||
providers: [UserProfileDialogService],
|
|
||||||
})
|
|
||||||
export class UserProfileModule {}
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { UserProfileDialogService } from './services/user-profile-dialog.service';
|
||||||
|
import { IqserRoutes } from '@iqser/common-ui';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard], providers: [UserProfileDialogService] },
|
||||||
|
] satisfies IqserRoutes;
|
||||||
95
apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts
Normal file
95
apps/red-ui/src/app/modules/account/utils/dialog-defaults.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { addHintTranslations } from '@translations/add-hint-translations';
|
||||||
|
import { redactTextTranslations } from '@translations/redact-text-translations';
|
||||||
|
import { removeRedactionTranslations } from '@translations/remove-redaction-translations';
|
||||||
|
import {
|
||||||
|
ForceAnnotationOptions,
|
||||||
|
RectangleRedactOptions,
|
||||||
|
RedactOrHintOptions,
|
||||||
|
RemoveRedactionOptions,
|
||||||
|
} from '../../file-preview/utils/dialog-types';
|
||||||
|
|
||||||
|
export const SystemDefaults = {
|
||||||
|
RECTANGLE_REDACT_DEFAULT: RectangleRedactOptions.ONLY_THIS_PAGE,
|
||||||
|
ADD_REDACTION_DEFAULT: RedactOrHintOptions.IN_DOSSIER,
|
||||||
|
ADD_HINT_DEFAULT: RedactOrHintOptions.IN_DOSSIER,
|
||||||
|
FORCE_REDACTION_DEFAULT: ForceAnnotationOptions.ONLY_HERE,
|
||||||
|
REMOVE_REDACTION_DEFAULT: RemoveRedactionOptions.ONLY_HERE,
|
||||||
|
REMOVE_HINT_DEFAULT: RemoveRedactionOptions.ONLY_HERE,
|
||||||
|
REMOVE_RECOMMENDATION_DEFAULT: RemoveRedactionOptions.DO_NOT_RECOMMEND,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const SystemDefaultOption = {
|
||||||
|
SYSTEM_DEFAULT: 'SYSTEM_DEFAULT',
|
||||||
|
label: _('dialog-defaults-form.system-default'),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type SystemDefaultType = typeof SystemDefaultOption.SYSTEM_DEFAULT;
|
||||||
|
|
||||||
|
export const redactionAddOptions = [
|
||||||
|
{
|
||||||
|
label: SystemDefaultOption.label,
|
||||||
|
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: redactTextTranslations.onlyHere.label,
|
||||||
|
value: RedactOrHintOptions.ONLY_HERE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: redactTextTranslations.inDocument.label,
|
||||||
|
value: RedactOrHintOptions.IN_DOCUMENT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: redactTextTranslations.inDossier.label,
|
||||||
|
value: RedactOrHintOptions.IN_DOSSIER,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const hintAddOptions = [
|
||||||
|
{
|
||||||
|
label: SystemDefaultOption.label,
|
||||||
|
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: addHintTranslations.onlyHere.label,
|
||||||
|
value: RedactOrHintOptions.ONLY_HERE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: addHintTranslations.inDossier.label,
|
||||||
|
value: RedactOrHintOptions.IN_DOSSIER,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const removeOptions = [
|
||||||
|
{
|
||||||
|
label: SystemDefaultOption.label,
|
||||||
|
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: removeRedactionTranslations.ONLY_HERE.label,
|
||||||
|
value: RemoveRedactionOptions.ONLY_HERE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: removeRedactionTranslations.IN_DOSSIER.label,
|
||||||
|
value: RemoveRedactionOptions.IN_DOSSIER,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const redactionRemoveOptions = [
|
||||||
|
...removeOptions,
|
||||||
|
{
|
||||||
|
label: removeRedactionTranslations.FALSE_POSITIVE.label,
|
||||||
|
value: RemoveRedactionOptions.FALSE_POSITIVE,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const recommendationRemoveOptions = [
|
||||||
|
{
|
||||||
|
label: SystemDefaultOption.label,
|
||||||
|
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: removeRedactionTranslations.DO_NOT_RECOMMEND.label,
|
||||||
|
value: RemoveRedactionOptions.DO_NOT_RECOMMEND,
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -1,282 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
|
||||||
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
|
||||||
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
|
||||||
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
|
||||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
|
||||||
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
|
||||||
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
|
||||||
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
|
||||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
|
||||||
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
|
|
||||||
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
|
||||||
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
|
||||||
import { PermissionsGuard } from '@guards/permissions-guard';
|
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
|
||||||
|
|
||||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
|
||||||
{
|
|
||||||
path: 'info',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () => import('./screens/info/dossier-template-info.module').then(m => m.DossierTemplateInfoModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'entities',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: EntitiesListingScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: `:${ENTITY_TYPE}`,
|
|
||||||
component: BaseEntityScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, entityExistsGuard()],
|
|
||||||
loadChildren: () => import('./screens/entities/entities.module').then(m => m.EntitiesModule),
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'entity-rules',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.rules.read],
|
|
||||||
redirectTo: 'info',
|
|
||||||
},
|
|
||||||
type: 'ENTITY',
|
|
||||||
},
|
|
||||||
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'component-rules',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.rules.read],
|
|
||||||
redirectTo: 'info',
|
|
||||||
},
|
|
||||||
type: 'COMPONENT',
|
|
||||||
},
|
|
||||||
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'file-attributes',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () =>
|
|
||||||
import('./screens/file-attributes-listing/file-attributes-listing.module').then(m => m.FileAttributesListingModule),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'watermarks',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'reports',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () => import('./screens/reports/reports.module').then(m => m.ReportsModule),
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.reportTemplates.read],
|
|
||||||
redirectTo: '/auth-error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-attributes',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () =>
|
|
||||||
import('./screens/dossier-attributes-listing/dossier-attributes-listing.module').then(m => m.DossierAttributesListingModule),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-states',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () =>
|
|
||||||
import('./screens/dossier-states-listing/dossier-states-listing.module').then(m => m.DossierStatesListingModule),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'default-colors',
|
|
||||||
component: DefaultColorsScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'justifications',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule),
|
|
||||||
canActivate: [IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.legalBasis.read],
|
|
||||||
redirectTo: '/auth-error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ path: '', redirectTo: 'info', pathMatch: 'full' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const dossierTemplatesRoutes: IqserRoutes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
canActivate: [IqserAuthGuard],
|
|
||||||
loadChildren: () =>
|
|
||||||
import('./screens/dossier-templates-listing/dossier-templates-listing.module').then(m => m.DossierTemplatesListingModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: `:${DOSSIER_TEMPLATE_ID}`,
|
|
||||||
children: dossierTemplateIdRoutes,
|
|
||||||
canActivate: [templateExistsWhenEnteringAdmin()],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const routes: IqserRoutes = [
|
|
||||||
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
|
||||||
{
|
|
||||||
path: 'dossier-templates',
|
|
||||||
children: dossierTemplatesRoutes,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard],
|
|
||||||
requiredRoles: ['RED_MANAGER', 'RED_ADMIN'],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.templates.read],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'users',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: UserListingScreenComponent,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.users.read, 'RED_USER_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-permissions',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, PermissionsGuard],
|
|
||||||
permissionsObject: 'Dossier',
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.manageAclPermissions, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loadChildren: () => import('./screens/permissions/permissions.module').then(m => m.PermissionsModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'license-info',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.license.readReport, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loadChildren: () => import('./screens/license/license.module').then(m => m.LicenseModule),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'digital-signature',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: DigitalSignatureScreenComponent,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.digitalSignature.read, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'audit',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: AuditScreenComponent,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.searchAudit, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'general-config',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: GeneralConfigScreenComponent,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.generalConfiguration.read, Roles.smtp.read, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [RouterModule.forChild(routes)],
|
|
||||||
exports: [RouterModule],
|
|
||||||
})
|
|
||||||
export class AdminRoutingModule {}
|
|
||||||
@ -1,130 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { AdminRoutingModule } from './admin-routing.module';
|
|
||||||
import { SharedModule } from '@shared/shared.module';
|
|
||||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
|
||||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
|
||||||
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
|
||||||
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
|
||||||
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
|
||||||
import { DossierTemplateBreadcrumbsComponent } from './shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
|
||||||
import { AddEditCloneDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component';
|
|
||||||
import { AddEntityDialogComponent } from './dialogs/add-entity-dialog/add-entity-dialog.component';
|
|
||||||
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
|
|
||||||
import { AdminDialogService } from './services/admin-dialog.service';
|
|
||||||
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
|
||||||
import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
|
||||||
import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
|
|
||||||
import { UsersStatsComponent } from './components/users-stats/users-stats.component';
|
|
||||||
import { ResetPasswordComponent } from './dialogs/add-edit-user-dialog/reset-password/reset-password.component';
|
|
||||||
import { UserDetailsComponent } from './dialogs/add-edit-user-dialog/user-details/user-details.component';
|
|
||||||
import { AuditService } from './services/audit.service';
|
|
||||||
import { DigitalSignatureService } from './services/digital-signature.service';
|
|
||||||
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
|
||||||
import { RulesService } from './services/rules.service';
|
|
||||||
import { SmtpConfigService } from './services/smtp-config.service';
|
|
||||||
import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
|
|
||||||
import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component';
|
|
||||||
import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component';
|
|
||||||
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
|
||||||
import { A11yModule } from '@angular/cdk/a11y';
|
|
||||||
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
|
||||||
import { AdminSideNavComponent } from './shared/components/admin-side-nav/admin-side-nav.component';
|
|
||||||
import { SystemPreferencesFormComponent } from './screens/general-config/system-preferences-form/system-preferences-form.component';
|
|
||||||
import { ConfigureCertificateDialogComponent } from './dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component';
|
|
||||||
import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
|
||||||
import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
|
|
||||||
import {
|
|
||||||
ChevronButtonComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
DetailsRadioComponent,
|
|
||||||
EditableInputComponent,
|
|
||||||
EmptyStateComponent,
|
|
||||||
HasScrollbarDirective,
|
|
||||||
HumanizePipe,
|
|
||||||
IconButtonComponent,
|
|
||||||
InputWithActionComponent,
|
|
||||||
IqserAllowDirective,
|
|
||||||
IqserDenyDirective,
|
|
||||||
IqserHelpModeModule,
|
|
||||||
IqserListingModule,
|
|
||||||
IqserUploadFileModule,
|
|
||||||
RoundCheckboxComponent,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { AuditInfoDialogComponent } from './dialogs/audit-info-dialog/audit-info-dialog.component';
|
|
||||||
import { DossierTemplateActionsComponent } from './shared/components/dossier-template-actions/dossier-template-actions.component';
|
|
||||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
|
||||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
|
||||||
import { SelectComponent } from '@shared/components/select/select.component';
|
|
||||||
|
|
||||||
const dialogs = [
|
|
||||||
AddEditCloneDossierTemplateDialogComponent,
|
|
||||||
AddEntityDialogComponent,
|
|
||||||
EditColorDialogComponent,
|
|
||||||
SmtpAuthDialogComponent,
|
|
||||||
AddEditUserDialogComponent,
|
|
||||||
UploadDictionaryDialogComponent,
|
|
||||||
ConfigureCertificateDialogComponent,
|
|
||||||
AuditInfoDialogComponent,
|
|
||||||
];
|
|
||||||
|
|
||||||
const screens = [
|
|
||||||
AuditScreenComponent,
|
|
||||||
DefaultColorsScreenComponent,
|
|
||||||
EntitiesListingScreenComponent,
|
|
||||||
DigitalSignatureScreenComponent,
|
|
||||||
UserListingScreenComponent,
|
|
||||||
GeneralConfigScreenComponent,
|
|
||||||
];
|
|
||||||
|
|
||||||
const components = [
|
|
||||||
UsersStatsComponent,
|
|
||||||
ResetPasswordComponent,
|
|
||||||
UserDetailsComponent,
|
|
||||||
BaseAdminScreenComponent,
|
|
||||||
BaseDossierTemplateScreenComponent,
|
|
||||||
BaseEntityScreenComponent,
|
|
||||||
GeneralConfigFormComponent,
|
|
||||||
SmtpFormComponent,
|
|
||||||
SystemPreferencesFormComponent,
|
|
||||||
PkcsSignatureConfigurationComponent,
|
|
||||||
KmsSignatureConfigurationComponent,
|
|
||||||
|
|
||||||
...dialogs,
|
|
||||||
...screens,
|
|
||||||
];
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [...components],
|
|
||||||
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService],
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
SharedModule,
|
|
||||||
AdminRoutingModule,
|
|
||||||
A11yModule,
|
|
||||||
IqserUsersModule,
|
|
||||||
TranslateModule,
|
|
||||||
HumanizePipe,
|
|
||||||
IqserListingModule,
|
|
||||||
IqserUploadFileModule,
|
|
||||||
IqserHelpModeModule,
|
|
||||||
AdminSideNavComponent,
|
|
||||||
DossierTemplateActionsComponent,
|
|
||||||
DossierTemplateBreadcrumbsComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
ChevronButtonComponent,
|
|
||||||
EmptyStateComponent,
|
|
||||||
HasScrollbarDirective,
|
|
||||||
RoundCheckboxComponent,
|
|
||||||
InputWithActionComponent,
|
|
||||||
EditableInputComponent,
|
|
||||||
DetailsRadioComponent,
|
|
||||||
IqserAllowDirective,
|
|
||||||
IqserDenyDirective,
|
|
||||||
TenantPipe,
|
|
||||||
SelectComponent,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class AdminModule {}
|
|
||||||
302
apps/red-ui/src/app/modules/admin/admin.routes.ts
Normal file
302
apps/red-ui/src/app/modules/admin/admin.routes.ts
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import { inject, provideEnvironmentInitializer } from '@angular/core';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
|
||||||
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
|
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
||||||
|
import { PermissionsGuard } from '@guards/permissions-guard';
|
||||||
|
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||||
|
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
|
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||||
|
import { CopilotService } from '@services/copilot.service';
|
||||||
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
|
import { Roles } from '@users/roles';
|
||||||
|
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
||||||
|
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
||||||
|
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||||
|
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||||
|
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||||
|
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
||||||
|
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
||||||
|
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
||||||
|
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
||||||
|
import { AdminDialogService } from './services/admin-dialog.service';
|
||||||
|
import { AuditService } from './services/audit.service';
|
||||||
|
import { DigitalSignatureService } from './services/digital-signature.service';
|
||||||
|
import { RulesService } from './services/rules.service';
|
||||||
|
import { SmtpConfigService } from './services/smtp-config.service';
|
||||||
|
|
||||||
|
const entityRoutes: IqserRoutes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
children: [{ path: '', component: EntitiesListingScreenComponent }],
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `:${ENTITY_TYPE}`,
|
||||||
|
component: BaseEntityScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, entityExistsGuard()],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
loadChildren: () => import('./screens/entities/entities.routes'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||||
|
{ path: '', redirectTo: 'info', pathMatch: 'full' },
|
||||||
|
{
|
||||||
|
path: 'info',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
loadComponent: () => import('./screens/info/dossier-template-info-screen/dossier-template-info-screen.component'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'entities',
|
||||||
|
children: entityRoutes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'entity-rules',
|
||||||
|
loadComponent: () => import('./screens/rules/rules-screen/rules-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.rules.read],
|
||||||
|
redirectTo: 'info',
|
||||||
|
},
|
||||||
|
type: 'ENTITY',
|
||||||
|
},
|
||||||
|
providers: [
|
||||||
|
RulesService,
|
||||||
|
provideEnvironmentInitializer(() => {
|
||||||
|
return inject(CopilotService).connectAsync('/api/llm/llm-websocket');
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'component-rules',
|
||||||
|
loadComponent: () => import('./screens/rules/rules-screen/rules-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.rules.read],
|
||||||
|
redirectTo: 'info',
|
||||||
|
},
|
||||||
|
type: 'COMPONENT',
|
||||||
|
},
|
||||||
|
providers: [RulesService],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'component-mappings',
|
||||||
|
loadComponent: () => import('./screens/component-mappings/component-mappings-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'components',
|
||||||
|
loadComponent: () => import('./screens/component-definitions/component-definitions.component'),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'file-attributes',
|
||||||
|
loadComponent: () => import('./screens/file-attributes-listing/file-attributes-listing-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'watermarks',
|
||||||
|
loadChildren: () => import('./screens/watermark/watermark.routes'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'reports',
|
||||||
|
loadComponent: () => import('./screens/reports/reports-screen/reports-screen.component'),
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.reportTemplates.read],
|
||||||
|
redirectTo: '/auth-error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-attributes',
|
||||||
|
loadComponent: () => import('./screens/dossier-attributes-listing/dossier-attributes-listing-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-states',
|
||||||
|
loadComponent: () =>
|
||||||
|
import('./screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'default-colors',
|
||||||
|
component: DefaultColorsScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'justifications',
|
||||||
|
loadComponent: () => import('./screens/justifications/justifications-screen/justifications-screen.component'),
|
||||||
|
canActivate: [IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.legalBasis.read],
|
||||||
|
redirectTo: '/auth-error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const dossierTemplatesRoutes: IqserRoutes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
loadComponent: () =>
|
||||||
|
import(
|
||||||
|
'./screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canActivate: [IqserAuthGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `:${DOSSIER_TEMPLATE_ID}`,
|
||||||
|
children: dossierTemplateIdRoutes,
|
||||||
|
canActivate: [templateExistsWhenEnteringAdmin()],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
||||||
|
{
|
||||||
|
path: 'dossier-templates',
|
||||||
|
children: dossierTemplatesRoutes,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard],
|
||||||
|
requiredRoles: ['RED_MANAGER', 'RED_ADMIN'],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.templates.read],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
providers: [AdminDialogService],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'users',
|
||||||
|
component: UserListingScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.users.read, 'RED_USER_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-permissions',
|
||||||
|
loadComponent: () => import('./screens/permissions/permissions-screen/permissions-screen.component'),
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard, PermissionsGuard],
|
||||||
|
permissionsObject: 'Dossier',
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.manageAclPermissions, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'license-info',
|
||||||
|
loadChildren: () => import('./screens/license/license.routes'),
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.license.readReport, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'digital-signature',
|
||||||
|
component: DigitalSignatureScreenComponent,
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.digitalSignature.read, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
providers: [DigitalSignatureService],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'audit',
|
||||||
|
component: AuditScreenComponent,
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.searchAudit, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
providers: [AuditService],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'general-config',
|
||||||
|
component: GeneralConfigScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.generalConfiguration.read, Roles.smtp.read, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
providers: [SmtpConfigService],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] satisfies IqserRoutes;
|
||||||
@ -1,7 +1,10 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-admin-screen.component.html',
|
templateUrl: './base-admin-screen.component.html',
|
||||||
styleUrls: ['./base-admin-screen.component.scss'],
|
styleUrls: ['./base-admin-screen.component.scss'],
|
||||||
|
imports: [AdminSideNavComponent, RouterOutlet],
|
||||||
})
|
})
|
||||||
export class BaseAdminScreenComponent {}
|
export class BaseAdminScreenComponent {}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
<!--TODO: Use this for all dossier template screens -->
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|||||||
@ -1,7 +1,22 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { DossierTemplateBreadcrumbsComponent } from '../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
||||||
|
import { DossierTemplateActionsComponent } from '../shared/components/dossier-template-actions/dossier-template-actions.component';
|
||||||
|
import { CircleButtonComponent } from '@iqser/common-ui';
|
||||||
|
import { RouterLink, RouterOutlet } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-dossier-template-screen.component.html',
|
templateUrl: './base-dossier-template-screen.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [
|
||||||
|
DossierTemplateBreadcrumbsComponent,
|
||||||
|
DossierTemplateActionsComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
RouterLink,
|
||||||
|
TranslateModule,
|
||||||
|
AdminSideNavComponent,
|
||||||
|
RouterOutlet,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class BaseDossierTemplateScreenComponent {}
|
export class BaseDossierTemplateScreenComponent {}
|
||||||
|
|||||||
@ -1,26 +1,39 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||||
import { Router } from '@angular/router';
|
import { Router, RouterLink, RouterOutlet } from '@angular/router';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
import { AdminDialogService } from '../services/admin-dialog.service';
|
import { AdminDialogService } from '../services/admin-dialog.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { LoadingService } from '@iqser/common-ui';
|
import { CircleButtonComponent, LoadingService } from '@iqser/common-ui';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||||
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
import { DossierTemplateBreadcrumbsComponent } from '../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
||||||
|
import { AsyncPipe, NgIf } from '@angular/common';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-entity-screen.component.html',
|
templateUrl: './base-entity-screen.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [
|
||||||
|
DossierTemplateBreadcrumbsComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
NgIf,
|
||||||
|
AsyncPipe,
|
||||||
|
TranslateModule,
|
||||||
|
RouterLink,
|
||||||
|
AdminSideNavComponent,
|
||||||
|
RouterOutlet,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class BaseEntityScreenComponent implements OnInit {
|
export class BaseEntityScreenComponent implements OnInit {
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
|
||||||
readonly #entityType = getParam(ENTITY_TYPE);
|
|
||||||
readonly disabledItems$: Observable<string[]>;
|
readonly disabledItems$: Observable<string[]>;
|
||||||
readonly canDeleteEntity$: Observable<boolean>;
|
readonly canDeleteEntity$: Observable<boolean>;
|
||||||
|
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||||
|
readonly #entityType = getParam(ENTITY_TYPE);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
@ -29,7 +42,6 @@ export class BaseEntityScreenComponent implements OnInit {
|
|||||||
private readonly _dialogService: AdminDialogService,
|
private readonly _dialogService: AdminDialogService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _tenantsService: TenantsService,
|
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
) {
|
) {
|
||||||
const entity$ = dictionaryMapService.watch$(this.#dossierTemplateId, this.#entityType);
|
const entity$ = dictionaryMapService.watch$(this.#dossierTemplateId, this.#entityType);
|
||||||
@ -50,7 +62,7 @@ export class BaseEntityScreenComponent implements OnInit {
|
|||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const dossierTemplate = this._dossierTemplatesService.find(this.#dossierTemplateId);
|
const dossierTemplate = this._dossierTemplatesService.find(this.#dossierTemplateId);
|
||||||
await firstValueFrom(this._dictionaryService.deleteDictionaries([this.#entityType], this.#dossierTemplateId));
|
await firstValueFrom(this._dictionaryService.deleteDictionaries([this.#entityType], this.#dossierTemplateId));
|
||||||
await this._router.navigate([`/${this._tenantsService.activeTenantId}/${dossierTemplate.routerLink}/entities`]);
|
await this._router.navigate([`/${dossierTemplate.routerLink}/entities`]);
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div class="mt-44">
|
<div class="mt-44">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
[config]="chartConfig"
|
[config]="chartConfig()"
|
||||||
[radius]="63"
|
[radius]="63"
|
||||||
[strokeWidth]="15"
|
[strokeWidth]="15"
|
||||||
[subtitles]="['user-stats.chart.users' | translate]"
|
[subtitles]="['user-stats.chart.users' | translate]"
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, input, Input, output, Output } from '@angular/core';
|
||||||
import { DonutChartConfig } from '@red/domain';
|
import { DonutChartConfig } from '@red/domain';
|
||||||
|
import { CircleButtonComponent } from '@iqser/common-ui';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-users-stats',
|
selector: 'redaction-users-stats',
|
||||||
templateUrl: './users-stats.component.html',
|
templateUrl: './users-stats.component.html',
|
||||||
styleUrls: ['./users-stats.component.scss'],
|
styleUrls: ['./users-stats.component.scss'],
|
||||||
|
imports: [CircleButtonComponent, TranslateModule, DonutChartComponent],
|
||||||
})
|
})
|
||||||
export class UsersStatsComponent {
|
export class UsersStatsComponent {
|
||||||
@Output() toggleCollapse = new EventEmitter();
|
readonly chartConfig = input.required<DonutChartConfig[]>();
|
||||||
@Input() chartConfig: DonutChartConfig[];
|
readonly toggleCollapse = output();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
<section class="dialog">
|
||||||
|
<div [innerHTML]="'add-clone-dossier-template.title' | translate: translateParams" class="dialog-header heading-l"></div>
|
||||||
|
|
||||||
|
<form [formGroup]="form">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<div class="iqser-input-group required w-300">
|
||||||
|
<label [translate]="'add-edit-clone-dossier-template.form.name'"></label>
|
||||||
|
<input
|
||||||
|
[placeholder]="'add-edit-clone-dossier-template.form.name-placeholder' | translate"
|
||||||
|
formControlName="name"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="iqser-input-group w-400">
|
||||||
|
<label [translate]="'add-edit-clone-dossier-template.form.description'"></label>
|
||||||
|
<textarea
|
||||||
|
[placeholder]="'add-edit-clone-dossier-template.form.description-placeholder' | translate"
|
||||||
|
formControlName="description"
|
||||||
|
name="description"
|
||||||
|
rows="4"
|
||||||
|
type="text"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<iqser-icon-button
|
||||||
|
(action)="save()"
|
||||||
|
[buttonId]="'saveButton'"
|
||||||
|
[disabled]="disabled"
|
||||||
|
[label]="'add-clone-dossier-template.save' | translate: translateParams"
|
||||||
|
[type]="iconButtonTypes.primary"
|
||||||
|
></iqser-icon-button>
|
||||||
|
|
||||||
|
<iqser-icon-button
|
||||||
|
(action)="save({ nextAction: true })"
|
||||||
|
[buttonId]="'saveButton'"
|
||||||
|
[disabled]="disabled"
|
||||||
|
[label]="'add-clone-dossier-template.save-and-edit' | translate"
|
||||||
|
[type]="iconButtonTypes.dark"
|
||||||
|
></iqser-icon-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
|
</section>
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { BaseDialogComponent, CircleButtonComponent, getConfig, IconButtonComponent, SaveOptions } from '@iqser/common-ui';
|
||||||
|
import { DossierTemplate } from '@red/domain';
|
||||||
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
export interface CloneTemplateData {
|
||||||
|
dossierTemplateId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './add-clone-dossier-template-dialog.component.html',
|
||||||
|
styleUrls: ['./add-clone-dossier-template-dialog.component.scss'],
|
||||||
|
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent],
|
||||||
|
})
|
||||||
|
export class AddCloneDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||||
|
readonly dossierTemplate?: DossierTemplate;
|
||||||
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
readonly translateParams: { type: string; dossierTemplateName?: string };
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
|
private readonly _router: Router,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddCloneDossierTemplateDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) readonly data: CloneTemplateData,
|
||||||
|
) {
|
||||||
|
super(_dialogRef);
|
||||||
|
this.dossierTemplate = this._dossierTemplatesService.find(this.data.dossierTemplateId);
|
||||||
|
|
||||||
|
this.translateParams = {
|
||||||
|
type: this.dossierTemplate ? 'clone' : 'create',
|
||||||
|
dossierTemplateName: this.dossierTemplate?.name,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.form = this.#getForm();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
override get disabled(): boolean {
|
||||||
|
// Ignore 'changed' value, doesn't make sense in this context
|
||||||
|
return !this.valid || this._hasErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(options?: SaveOptions): Promise<void> {
|
||||||
|
let dossierTemplate: DossierTemplate;
|
||||||
|
this._loadingService.start();
|
||||||
|
const body = {
|
||||||
|
...this.dossierTemplate,
|
||||||
|
...this.form.getRawValue(),
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
if (this.dossierTemplate) {
|
||||||
|
dossierTemplate = await this._dossierTemplatesService.clone(this.dossierTemplate.id, body);
|
||||||
|
} else {
|
||||||
|
dossierTemplate = await this._dossierTemplatesService.createOrUpdate(body);
|
||||||
|
}
|
||||||
|
if (options?.nextAction) {
|
||||||
|
await this._router.navigate([dossierTemplate.routerLink]);
|
||||||
|
}
|
||||||
|
this._dialogRef.close(true);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
|
this._toaster.error(_('add-edit-clone-dossier-template.error.conflict'), { error });
|
||||||
|
} else {
|
||||||
|
this._toaster.rawError(error.error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
#getForm() {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
name: [this.dossierTemplate ? this.#getCloneName(this.dossierTemplate) : undefined, Validators.required],
|
||||||
|
description: [this.dossierTemplate?.description],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#getCloneName(initialTemplate: DossierTemplate): string {
|
||||||
|
const templateName = initialTemplate.name.trim();
|
||||||
|
let nameOfClonedTemplate: string = templateName.split('Copy of ').filter(n => n)[0];
|
||||||
|
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
||||||
|
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
||||||
|
|
||||||
|
let clonesCount = 0;
|
||||||
|
for (const name of allTemplatesNames) {
|
||||||
|
const splitName = name.split(nameOfClonedTemplate);
|
||||||
|
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
||||||
|
if (splitName[0] === 'Copy of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
||||||
|
clonesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clonesCount >= 1) {
|
||||||
|
return `Copy of ${nameOfClonedTemplate} (${clonesCount})`;
|
||||||
|
}
|
||||||
|
return `Copy of ${nameOfClonedTemplate}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,58 +0,0 @@
|
|||||||
.validity {
|
|
||||||
width: 230px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 16px;
|
|
||||||
|
|
||||||
mat-checkbox {
|
|
||||||
margin-right: 16px;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
min-height: 42px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iqser-input-group {
|
|
||||||
min-height: 42px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
redaction-select {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.download-includes {
|
|
||||||
margin: 16px 0 10px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-elements {
|
|
||||||
display: flex;
|
|
||||||
gap: 40px;
|
|
||||||
|
|
||||||
.iqser-input-group {
|
|
||||||
margin-top: 0;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
mat-checkbox {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
margin-left: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.half-flex-basis {
|
|
||||||
flex-basis: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pl-75 {
|
|
||||||
padding: 0 0 0 75px;
|
|
||||||
}
|
|
||||||
@ -1,173 +0,0 @@
|
|||||||
import { HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { Component, Inject } from '@angular/core';
|
|
||||||
import { AbstractControl, Validators } from '@angular/forms';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { BaseDialogComponent, getConfig } from '@iqser/common-ui';
|
|
||||||
import { DossierTemplate, IDossierTemplate } from '@red/domain';
|
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
|
||||||
import { downloadTypesTranslations } from '@translations/download-types-translations';
|
|
||||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
|
||||||
|
|
||||||
interface EditCloneTemplateData {
|
|
||||||
dossierTemplateId: string;
|
|
||||||
clone?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadTypes = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(type => ({
|
|
||||||
key: type,
|
|
||||||
label: downloadTypesTranslations[type],
|
|
||||||
}));
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './add-edit-clone-dossier-template-dialog.component.html',
|
|
||||||
styleUrls: ['./add-edit-clone-dossier-template-dialog.component.scss'],
|
|
||||||
})
|
|
||||||
export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogComponent {
|
|
||||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
hasValidFrom: boolean;
|
|
||||||
hasValidTo: boolean;
|
|
||||||
readonly downloadTypes = downloadTypes;
|
|
||||||
readonly dossierTemplate: DossierTemplate;
|
|
||||||
private _previousValidFrom: Dayjs;
|
|
||||||
private _previousValidTo: Dayjs;
|
|
||||||
private _lastValidFrom: Dayjs;
|
|
||||||
private _lastValidTo: Dayjs;
|
|
||||||
|
|
||||||
get disabled(): boolean {
|
|
||||||
if (!this.data?.clone) {
|
|
||||||
return super.disabled;
|
|
||||||
}
|
|
||||||
return !this.valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get translateParams() {
|
|
||||||
return {
|
|
||||||
type: this.dossierTemplate ? (this.data.clone ? 'clone' : 'edit') : 'create',
|
|
||||||
name: this.dossierTemplate?.name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
|
||||||
protected readonly _dialogRef: MatDialogRef<AddEditCloneDossierTemplateDialogComponent>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) readonly data: EditCloneTemplateData,
|
|
||||||
) {
|
|
||||||
super(_dialogRef, !!data && !data.clone);
|
|
||||||
this.dossierTemplate = this._dossierTemplatesService.find(this.data?.dossierTemplateId);
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
|
|
||||||
this.hasValidTo = !!this.dossierTemplate?.validTo;
|
|
||||||
|
|
||||||
this._previousValidFrom = this._lastValidFrom = this.form.get('validFrom').value;
|
|
||||||
this._previousValidTo = this._lastValidTo = this.form.get('validTo').value;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleHasValid(extremity: string) {
|
|
||||||
if (extremity === 'from') {
|
|
||||||
this.hasValidFrom = !this.hasValidFrom;
|
|
||||||
this.form.controls['validFrom'].setValue(this.hasValidFrom ? this._lastValidFrom : null);
|
|
||||||
} else {
|
|
||||||
this.hasValidTo = !this.hasValidTo;
|
|
||||||
this.form.controls['validTo'].setValue(this.hasValidTo ? this._lastValidTo : null);
|
|
||||||
}
|
|
||||||
this.applyValidityIntervalConstraints();
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
this._loadingService.start();
|
|
||||||
const dossierTemplate = {
|
|
||||||
dossierTemplateId: this.dossierTemplate?.dossierTemplateId,
|
|
||||||
...this.form.getRawValue(),
|
|
||||||
validFrom: this.hasValidFrom ? this.form.get('validFrom').value : null,
|
|
||||||
validTo: this.hasValidTo ? this.form.get('validTo').value : null,
|
|
||||||
} as IDossierTemplate;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.data?.clone) {
|
|
||||||
await this._dossierTemplatesService.clone(this.dossierTemplate.id, dossierTemplate);
|
|
||||||
} else {
|
|
||||||
await this._dossierTemplatesService.createOrUpdate(dossierTemplate);
|
|
||||||
}
|
|
||||||
this._dialogRef.close(true);
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
|
||||||
this._toaster.error(_('add-edit-clone-dossier-template.error.conflict'), { error });
|
|
||||||
} else {
|
|
||||||
this._toaster.rawError(error.error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
applyValidityIntervalConstraints(): void {
|
|
||||||
const formValue = this.form.value;
|
|
||||||
applyIntervalConstraints(formValue, this._previousValidFrom, this._previousValidTo, this.form, 'validFrom', 'validTo');
|
|
||||||
|
|
||||||
this._previousValidFrom = this.form.get('validFrom').value;
|
|
||||||
this._previousValidTo = this.form.get('validTo').value;
|
|
||||||
this._lastValidFrom = this._previousValidFrom || this._lastValidFrom;
|
|
||||||
this._lastValidTo = this._previousValidTo || this._lastValidTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm() {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
name: [this.#getCloneName(), Validators.required],
|
|
||||||
description: [this.dossierTemplate?.description],
|
|
||||||
validFrom: [
|
|
||||||
this.dossierTemplate?.validFrom ? dayjs(this.dossierTemplate?.validFrom) : null,
|
|
||||||
this.#requiredIfValidator(() => this.hasValidFrom),
|
|
||||||
],
|
|
||||||
validTo: [
|
|
||||||
this.dossierTemplate?.validTo ? dayjs(this.dossierTemplate?.validTo) : null,
|
|
||||||
this.#requiredIfValidator(() => this.hasValidTo),
|
|
||||||
],
|
|
||||||
applyDictionaryUpdatesToAllDossiersByDefault: [this.dossierTemplate?.applyDictionaryUpdatesToAllDossiersByDefault],
|
|
||||||
ocrByDefault: [this.dossierTemplate?.ocrByDefault],
|
|
||||||
removeWatermark: [this.dossierTemplate?.removeWatermark],
|
|
||||||
downloadFileTypes: [this.dossierTemplate?.downloadFileTypes || ['PREVIEW', 'REDACTED']],
|
|
||||||
keepHiddenText: [this.dossierTemplate?.keepHiddenText],
|
|
||||||
keepImageMetadata: [this.dossierTemplate?.keepImageMetadata],
|
|
||||||
keepOverlappingObjects: [this.dossierTemplate?.keepOverlappingObjects],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#getCloneName(): string {
|
|
||||||
if (!this.data?.clone) {
|
|
||||||
return this.dossierTemplate?.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
const templateName = this.dossierTemplate.name.trim();
|
|
||||||
let nameOfClonedTemplate: string = templateName.split('Copy of ').filter(n => n)[0];
|
|
||||||
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
|
||||||
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
|
||||||
|
|
||||||
let clonesCount = 0;
|
|
||||||
for (const name of allTemplatesNames) {
|
|
||||||
const splitName = name.split(nameOfClonedTemplate);
|
|
||||||
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
|
||||||
if (splitName[0] === 'Copy of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
|
||||||
clonesCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clonesCount >= 1) {
|
|
||||||
return `Copy of ${nameOfClonedTemplate} (${clonesCount})`;
|
|
||||||
}
|
|
||||||
return `Copy of ${nameOfClonedTemplate}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
#requiredIfValidator(predicate) {
|
|
||||||
return (formControl: AbstractControl) => {
|
|
||||||
if (!formControl.parent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (predicate()) {
|
|
||||||
return Validators.required(formControl);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +1,23 @@
|
|||||||
import { Component, Inject, ViewChild } from '@angular/core';
|
import { Component, Inject, ViewChild } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { BaseDialogComponent, CircleButtonComponent } from '@iqser/common-ui';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
|
import { ResetPasswordComponent } from './reset-password/reset-password.component';
|
||||||
import { UserDetailsComponent } from './user-details/user-details.component';
|
import { UserDetailsComponent } from './user-details/user-details.component';
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-add-edit-user-dialog',
|
selector: 'redaction-add-edit-user-dialog',
|
||||||
templateUrl: './add-edit-user-dialog.component.html',
|
templateUrl: './add-edit-user-dialog.component.html',
|
||||||
|
imports: [UserDetailsComponent, ResetPasswordComponent, CircleButtonComponent],
|
||||||
})
|
})
|
||||||
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
||||||
resettingPassword = false;
|
|
||||||
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
||||||
|
resettingPassword = false;
|
||||||
|
|
||||||
constructor(protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: User) {
|
constructor(
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) readonly user: User,
|
||||||
|
) {
|
||||||
super(_dialogRef, !!user);
|
super(_dialogRef, !!user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
import { IconButtonComponent, IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NamePipe } from '@common-ui/users/name.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-reset-password',
|
selector: 'redaction-reset-password',
|
||||||
templateUrl: './reset-password.component.html',
|
templateUrl: './reset-password.component.html',
|
||||||
|
imports: [TranslateModule, NamePipe, ReactiveFormsModule, IconButtonComponent],
|
||||||
})
|
})
|
||||||
export class ResetPasswordComponent {
|
export class ResetPasswordComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
@ -20,25 +22,20 @@ export class ResetPasswordComponent {
|
|||||||
private readonly _formBuilder: UntypedFormBuilder,
|
private readonly _formBuilder: UntypedFormBuilder,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _toaster: Toaster,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
try {
|
await firstValueFrom(
|
||||||
await firstValueFrom(
|
this._userService.resetPassword(
|
||||||
this._userService.resetPassword(
|
{
|
||||||
{
|
password: this.form.get('temporaryPassword').value,
|
||||||
password: this.form.get('temporaryPassword').value,
|
temporary: true,
|
||||||
temporary: true,
|
},
|
||||||
},
|
this.user.id,
|
||||||
this.user.id,
|
),
|
||||||
),
|
);
|
||||||
);
|
this.toggleResetPassword.emit();
|
||||||
this.toggleResetPassword.emit();
|
|
||||||
} catch (error) {
|
|
||||||
this._toaster.error(_('reset-password-dialog.error.password-policy'));
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<div
|
<div
|
||||||
[translateParams]="{
|
[translateParams]="{
|
||||||
type: user ? 'edit' : 'create'
|
type: !!user() ? 'edit' : 'create',
|
||||||
}"
|
}"
|
||||||
[translate]="'add-edit-user.title'"
|
[translate]="'add-edit-user.title'"
|
||||||
class="dialog-header heading-l"
|
class="dialog-header heading-l"
|
||||||
@ -32,14 +32,22 @@
|
|||||||
[formControlName]="role"
|
[formControlName]="role"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{{ translations[role] | translate }}
|
{{ translations[role] | translate: { count: 1 } }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (!user()) {
|
||||||
|
<div class="iqser-input-group">
|
||||||
|
<label [translate]="'add-edit-user.form.account-setup'"></label>
|
||||||
|
<mat-checkbox formControlName="sendSetPasswordMail">{{ 'add-edit-user.form.send-email' | translate }}</mat-checkbox>
|
||||||
|
<span [translate]="'add-edit-user.form.send-email-explanation'" class="hint"></span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
(click)="toggleResetPassword.emit()"
|
(click)="toggleResetPassword.emit()"
|
||||||
*ngIf="!!user"
|
*ngIf="!!user()"
|
||||||
[translate]="'add-edit-user.form.reset-password'"
|
[translate]="'add-edit-user.form.reset-password'"
|
||||||
class="mt-24 fit-content link-action"
|
class="mt-24 fit-content link-action"
|
||||||
></div>
|
></div>
|
||||||
@ -48,14 +56,14 @@
|
|||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
[disabled]="form.invalid || !changed"
|
[disabled]="form.invalid || !changed"
|
||||||
[label]="(user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate"
|
[label]="(user() ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="delete()"
|
(action)="delete()"
|
||||||
*ngIf="user && !disabledDelete(user)"
|
*ngIf="user() && !disabledDelete(user())"
|
||||||
[label]="'add-edit-user.actions.delete' | translate"
|
[label]="'add-edit-user.actions.delete' | translate"
|
||||||
[type]="iconButtonTypes.dark"
|
[type]="iconButtonTypes.dark"
|
||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
|
|||||||
@ -5,3 +5,7 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
margin-left: 23px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,29 +1,31 @@
|
|||||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
import { Component, input, OnInit, output } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { rolesTranslations } from '@translations/roles-translations';
|
import { rolesTranslations } from '@translations/roles-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-details',
|
selector: 'redaction-user-details',
|
||||||
templateUrl: './user-details.component.html',
|
templateUrl: './user-details.component.html',
|
||||||
styleUrls: ['./user-details.component.scss'],
|
styleUrls: ['./user-details.component.scss'],
|
||||||
|
imports: [TranslateModule, ReactiveFormsModule, MatCheckbox, NgForOf, IconButtonComponent, NgIf],
|
||||||
})
|
})
|
||||||
export class UserDetailsComponent extends BaseFormComponent implements OnChanges {
|
export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
||||||
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
user = input<User>();
|
||||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
readonly toggleResetPassword = output();
|
||||||
@Input() user: User;
|
readonly closeDialog = output<boolean>();
|
||||||
@Output() readonly toggleResetPassword = new EventEmitter();
|
readonly cancel = output();
|
||||||
@Output() readonly closeDialog = new EventEmitter();
|
|
||||||
@Output() readonly cancel = new EventEmitter();
|
|
||||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||||
readonly translations = rolesTranslations;
|
readonly translations = rolesTranslations;
|
||||||
|
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
||||||
|
readonly #ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: UntypedFormBuilder,
|
private readonly _formBuilder: UntypedFormBuilder,
|
||||||
@ -44,13 +46,17 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _rolesControls(): any {
|
get sendSetPasswordMail() {
|
||||||
|
return !this.form.controls.sendSetPasswordMail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get #rolesControls() {
|
||||||
return this.ROLES.reduce(
|
return this.ROLES.reduce(
|
||||||
(prev, role) => ({
|
(prev, role) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[role]: [
|
[role]: [
|
||||||
{
|
{
|
||||||
value: this.user && this.user.has(role),
|
value: this.user() && this.user().has(role),
|
||||||
disabled: this.shouldBeDisabled(role),
|
disabled: this.shouldBeDisabled(role),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -59,20 +65,20 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnInit() {
|
||||||
this.form = this._getForm();
|
this.form = this.#getForm();
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeDisabled(role: string): boolean {
|
shouldBeDisabled(role: string): boolean {
|
||||||
const isCurrentAdminUser = this.user && this.user.isAdmin && this.user.id === this._userService.currentUser.id;
|
const isCurrentAdminUser = this.user() && this.user().isAdmin && this.user().id === this._userService.currentUser.id;
|
||||||
return (
|
return (
|
||||||
// RED_ADMIN can't remove own RED_ADMIN role
|
// RED_ADMIN can't remove own RED_ADMIN role
|
||||||
(role === 'RED_ADMIN' && isCurrentAdminUser) ||
|
(role === 'RED_ADMIN' && isCurrentAdminUser) ||
|
||||||
// only RED_ADMINs can edit RED_ADMIN roles
|
// only RED_ADMINs can edit RED_ADMIN roles
|
||||||
(role === 'RED_ADMIN' && !this._userService.currentUser.isAdmin) ||
|
(role === 'RED_ADMIN' && !this._userService.currentUser.isAdmin) ||
|
||||||
Object.keys(this._ROLE_REQUIREMENTS).reduce(
|
Object.keys(this.#ROLE_REQUIREMENTS).reduce(
|
||||||
(value, key) => value || (role === this._ROLE_REQUIREMENTS[key] && this.user?.roles.includes(key)),
|
(value, key) => value || (role === this.#ROLE_REQUIREMENTS[key] && this.user()?.roles.includes(key)),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -80,38 +86,38 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
|
|||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const userData: IProfileUpdateRequest = { ...this.form.getRawValue(), roles: this.activeRoles };
|
const userData: IProfileUpdateRequest = {
|
||||||
|
...this.form.getRawValue(),
|
||||||
|
roles: this.activeRoles,
|
||||||
|
sendSetPasswordMail: this.sendSetPasswordMail,
|
||||||
|
};
|
||||||
|
|
||||||
if (!this.user) {
|
if (!this.user()) {
|
||||||
await firstValueFrom(this._userService.create(userData))
|
await firstValueFrom(this._userService.create(userData))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
this._toaster.error(null, { error });
|
||||||
this._toaster.error(_('add-edit-user.error.email-already-used'));
|
|
||||||
} else {
|
|
||||||
this._toaster.error(_('add-edit-user.error.generic'));
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await firstValueFrom(this._userService.updateProfile(userData, this.user.id));
|
await firstValueFrom(this._userService.updateProfile(userData, this.user().id));
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this._dialogService.deleteUsers([this.user.id], () => this.closeDialog.emit(true));
|
this._dialogService.deleteUsers([this.user().id], () => this.closeDialog.emit(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
setRolesRequirements(checked: boolean, role: string): void {
|
setRolesRequirements(checked: boolean, role: string): void {
|
||||||
if (Object.keys(this._ROLE_REQUIREMENTS).includes(role)) {
|
if (Object.keys(this.#ROLE_REQUIREMENTS).includes(role)) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.form.patchValue({ [this._ROLE_REQUIREMENTS[role]]: true });
|
this.form.patchValue({ [this.#ROLE_REQUIREMENTS[role]]: true });
|
||||||
this.form.controls[this._ROLE_REQUIREMENTS[role]].disable();
|
this.form.controls[this.#ROLE_REQUIREMENTS[role]].disable();
|
||||||
} else {
|
} else {
|
||||||
this.form.controls[this._ROLE_REQUIREMENTS[role]].enable();
|
this.form.controls[this.#ROLE_REQUIREMENTS[role]].enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,18 +128,19 @@ export class UserDetailsComponent extends BaseFormComponent implements OnChanges
|
|||||||
return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin);
|
return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): UntypedFormGroup {
|
#getForm(): UntypedFormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
firstName: [this.user?.firstName, Validators.required],
|
firstName: [this.user()?.firstName, Validators.required],
|
||||||
lastName: [this.user?.lastName, Validators.required],
|
lastName: [this.user()?.lastName, Validators.required],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
value: this.user?.email,
|
value: this.user()?.email,
|
||||||
disabled: !!this.user?.email,
|
disabled: !!this.user()?.email,
|
||||||
},
|
},
|
||||||
[Validators.required, Validators.email],
|
[Validators.required, Validators.email],
|
||||||
],
|
],
|
||||||
...this._rolesControls,
|
...this.#rolesControls,
|
||||||
|
sendSetPasswordMail: [false],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<iqser-help-button *ngIf="!config.IS_DOCUMINE" [helpButtonKey]="'create_new_entity'"></iqser-help-button>
|
<iqser-help-button *ngIf="!config.IS_DOCUMINE"></iqser-help-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
|
|||||||
@ -1,7 +1,16 @@
|
|||||||
import { Component, inject, ViewChild } from '@angular/core';
|
import { Component, inject, ViewChild } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { BaseDialogComponent, getConfig, IconButtonTypes } from '@iqser/common-ui';
|
import {
|
||||||
|
BaseDialogComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
getConfig,
|
||||||
|
HelpButtonComponent,
|
||||||
|
IconButtonComponent,
|
||||||
|
IconButtonTypes,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
|
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
|
||||||
interface DialogData {
|
interface DialogData {
|
||||||
readonly dossierTemplateId: string;
|
readonly dossierTemplateId: string;
|
||||||
@ -10,6 +19,7 @@ interface DialogData {
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './add-entity-dialog.component.html',
|
templateUrl: './add-entity-dialog.component.html',
|
||||||
styleUrls: ['./add-entity-dialog.component.scss'],
|
styleUrls: ['./add-entity-dialog.component.scss'],
|
||||||
|
imports: [AddEditEntityComponent, TranslateModule, IconButtonComponent, NgIf, CircleButtonComponent, HelpButtonComponent],
|
||||||
})
|
})
|
||||||
export class AddEntityDialogComponent extends BaseDialogComponent {
|
export class AddEntityDialogComponent extends BaseDialogComponent {
|
||||||
@ViewChild(AddEditEntityComponent, { static: true }) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
@ViewChild(AddEditEntityComponent, { static: true }) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
||||||
@ -34,11 +44,7 @@ export class AddEntityDialogComponent extends BaseDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
try {
|
await this._addEditEntityComponent.save();
|
||||||
await this._addEditEntityComponent.save();
|
this._dialogRef.close(true);
|
||||||
this._dialogRef.close(true);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,9 +6,11 @@
|
|||||||
<div class="table-header">Key</div>
|
<div class="table-header">Key</div>
|
||||||
<div class="table-header">Value</div>
|
<div class="table-header">Value</div>
|
||||||
|
|
||||||
<ng-container *ngFor="let entry of data.auditEntry.details | keyvalue : originalOrder">
|
<ng-container *ngFor="let entry of data.auditEntry.details | keyvalue: originalOrder">
|
||||||
<div class="bold">{{ entry.key | humanize }}</div>
|
<div class="bold">{{ entry.key | humanize }}</div>
|
||||||
<div>{{ entry.value }}</div>
|
<div>
|
||||||
|
<pre>{{ entry.value | json }}</pre>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,30 +1,32 @@
|
|||||||
|
import { JsonPipe, KeyValue, KeyValuePipe, NgForOf } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { BaseDialogComponent, CircleButtonComponent, HumanizePipe } from '@iqser/common-ui';
|
||||||
import { IAudit } from '@red/domain';
|
import { IAudit } from '@red/domain';
|
||||||
import { DossierStatesService } from '@services/entity-services/dossier-states.service';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { KeyValue } from '@angular/common';
|
|
||||||
|
|
||||||
interface DialogData {
|
interface DialogData {
|
||||||
readonly auditEntry: IAudit;
|
readonly auditEntry: IAudit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrderFn = (a: KeyValue<string, string>, b: KeyValue<string, string>) => number;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-audit-info-dialog',
|
selector: 'redaction-audit-info-dialog',
|
||||||
templateUrl: './audit-info-dialog.component.html',
|
templateUrl: './audit-info-dialog.component.html',
|
||||||
styleUrls: ['./audit-info-dialog.component.scss'],
|
styleUrls: ['./audit-info-dialog.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [TranslateModule, NgForOf, KeyValuePipe, HumanizePipe, CircleButtonComponent, JsonPipe],
|
||||||
})
|
})
|
||||||
export class AuditInfoDialogComponent extends BaseDialogComponent {
|
export class AuditInfoDialogComponent extends BaseDialogComponent {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _dossierStatesService: DossierStatesService,
|
|
||||||
protected readonly _dialogRef: MatDialogRef<AuditInfoDialogComponent>,
|
protected readonly _dialogRef: MatDialogRef<AuditInfoDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
||||||
) {
|
) {
|
||||||
super(_dialogRef, false);
|
super(_dialogRef, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly originalOrder = (a: KeyValue<string, string>, b: KeyValue<string, string>): number => 0;
|
readonly originalOrder: OrderFn = () => 0;
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
|
import { NgIf } from '@angular/common';
|
||||||
import { digitalSignatureDialogTranslations } from '../../translations/digital-signature-dialog-translations';
|
|
||||||
import { BaseDialogComponent, DetailsRadioOption } from '@iqser/common-ui';
|
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { PkcsSignatureConfigurationComponent } from './form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
|
||||||
import { KmsSignatureConfigurationComponent } from './form/kms-signature-configuration/kms-signature-configuration.component';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
||||||
|
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
||||||
|
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { DigitalSignatureOption, DigitalSignatureOptions } from '@red/domain';
|
import { DigitalSignatureOption, DigitalSignatureOptions } from '@red/domain';
|
||||||
|
import { DigitalSignatureService } from '../../services/digital-signature.service';
|
||||||
|
import { digitalSignatureDialogTranslations } from '../../translations/digital-signature-dialog-translations';
|
||||||
|
import { KmsSignatureConfigurationComponent } from './form/kms-signature-configuration/kms-signature-configuration.component';
|
||||||
|
import { PkcsSignatureConfigurationComponent } from './form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
||||||
|
|
||||||
const DEFAULT_DIALOG_WIDTH = '662px';
|
const DEFAULT_DIALOG_WIDTH = '662px';
|
||||||
const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
||||||
@ -14,6 +20,18 @@ const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './configure-certificate-dialog.component.html',
|
templateUrl: './configure-certificate-dialog.component.html',
|
||||||
styleUrls: ['./configure-certificate-dialog.component.scss'],
|
styleUrls: ['./configure-certificate-dialog.component.scss'],
|
||||||
|
imports: [
|
||||||
|
DetailsRadioComponent,
|
||||||
|
NgIf,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
TranslateModule,
|
||||||
|
PkcsSignatureConfigurationComponent,
|
||||||
|
KmsSignatureConfigurationComponent,
|
||||||
|
IconButtonComponent,
|
||||||
|
MatDialogClose,
|
||||||
|
CircleButtonComponent,
|
||||||
|
],
|
||||||
|
providers: [DigitalSignatureService],
|
||||||
})
|
})
|
||||||
export class ConfigureCertificateDialogComponent extends BaseDialogComponent {
|
export class ConfigureCertificateDialogComponent extends BaseDialogComponent {
|
||||||
@ViewChild(PkcsSignatureConfigurationComponent) pkcsSignatureConfigurationComponent: PkcsSignatureConfigurationComponent;
|
@ViewChild(PkcsSignatureConfigurationComponent) pkcsSignatureConfigurationComponent: PkcsSignatureConfigurationComponent;
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { BaseFormComponent } from '@iqser/common-ui';
|
import { BaseFormComponent } from '@iqser/common-ui';
|
||||||
import { DigitalSignatureService } from '../../../services/digital-signature.service';
|
|
||||||
import { DigitalSignatureOption, DigitalSignatureOptions, IDigitalSignatureRequest } from '@red/domain';
|
import { DigitalSignatureOption, DigitalSignatureOptions, IDigitalSignatureRequest } from '@red/domain';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
|
import { DigitalSignatureService } from '../../../services/digital-signature.service';
|
||||||
|
|
||||||
|
// TODO: This should be handled with services for each type of signature
|
||||||
export abstract class BaseSignatureConfigurationComponent extends BaseFormComponent {
|
export abstract class BaseSignatureConfigurationComponent extends BaseFormComponent {
|
||||||
file: File | null;
|
file: File | null;
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,27 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
||||||
import { DigitalSignatureOptions, IKmsDigitalSignature, IKmsDigitalSignatureRequest } from '@red/domain';
|
import { DigitalSignatureOptions, IKmsDigitalSignature, IKmsDigitalSignatureRequest } from '@red/domain';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
||||||
|
import { UploadFileComponent } from '@iqser/common-ui';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-kms-signature-configuration',
|
selector: 'redaction-kms-signature-configuration',
|
||||||
templateUrl: './kms-signature-configuration.component.html',
|
templateUrl: './kms-signature-configuration.component.html',
|
||||||
styleUrls: ['./kms-signature-configuration.component.scss'],
|
styleUrls: ['./kms-signature-configuration.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
|
||||||
})
|
})
|
||||||
export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
||||||
@Input() digitalSignature!: IKmsDigitalSignatureRequest;
|
@Input() digitalSignature!: IKmsDigitalSignatureRequest;
|
||||||
|
|
||||||
constructor(protected readonly _digitalSignatureService: DigitalSignatureService, private readonly _formBuilder: UntypedFormBuilder) {
|
constructor(
|
||||||
|
protected readonly _digitalSignatureService: DigitalSignatureService,
|
||||||
|
private readonly _formBuilder: UntypedFormBuilder,
|
||||||
|
) {
|
||||||
super(_digitalSignatureService, DigitalSignatureOptions.KMS);
|
super(_digitalSignatureService, DigitalSignatureOptions.KMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,28 +5,29 @@
|
|||||||
[readonly]="!!file"
|
[readonly]="!!file"
|
||||||
accept=".p12"
|
accept=".p12"
|
||||||
></iqser-upload-file>
|
></iqser-upload-file>
|
||||||
|
|
||||||
<form [formGroup]="form">
|
<form [formGroup]="form">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex fields-container">
|
<div class="flex fields-container">
|
||||||
<div class="iqser-input-group required w-300">
|
<div class="iqser-input-group required w-300">
|
||||||
<label [translate]="'digital-signature-dialog.forms.pkcs.certificate-name'"></label>
|
<label [translate]="'digital-signature-dialog.forms.pkcs.certificate-name'"></label>
|
||||||
<input id="certificate-name-input" formControlName="certificateName" type="text" />
|
<input formControlName="certificateName" id="certificate-name-input" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!digitalSignature" class="iqser-input-group required w-300">
|
<div *ngIf="!digitalSignature" class="iqser-input-group required w-300">
|
||||||
<label [translate]="'digital-signature-dialog.forms.pkcs.password-key'"></label>
|
<label [translate]="'digital-signature-dialog.forms.pkcs.password-key'"></label>
|
||||||
<input id="certificate-password-input" formControlName="password" type="password" />
|
<input formControlName="password" id="certificate-password-input" type="password" />
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group w-300">
|
<div class="iqser-input-group w-300">
|
||||||
<label [translate]="'digital-signature-dialog.forms.pkcs.contact-information'"></label>
|
<label [translate]="'digital-signature-dialog.forms.pkcs.contact-information'"></label>
|
||||||
<input id="certificate-contact-info-input" formControlName="contactInfo" type="text" />
|
<input formControlName="contactInfo" id="certificate-contact-info-input" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group w-300">
|
<div class="iqser-input-group w-300">
|
||||||
<label [translate]="'digital-signature-dialog.forms.pkcs.location'"></label>
|
<label [translate]="'digital-signature-dialog.forms.pkcs.location'"></label>
|
||||||
<input id="certificate-location-input" formControlName="location" type="text" />
|
<input formControlName="location" id="certificate-location-input" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group w-450">
|
<div class="iqser-input-group w-450">
|
||||||
<label [translate]="'digital-signature-dialog.forms.pkcs.reason'"></label>
|
<label [translate]="'digital-signature-dialog.forms.pkcs.reason'"></label>
|
||||||
<textarea id="certificate-location-input" formControlName="reason" rows="4" type="text"></textarea>
|
<textarea formControlName="reason" id="certificate-reason-input" rows="4" type="text"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,21 +1,28 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
|
||||||
import { DigitalSignatureOptions, IPkcsDigitalSignature, IPkcsDigitalSignatureRequest } from '@red/domain';
|
import { DigitalSignatureOptions, IPkcsDigitalSignature, IPkcsDigitalSignatureRequest } from '@red/domain';
|
||||||
|
import { lastIndexOfEnd } from '@utils/functions';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
||||||
import { lastIndexOfEnd } from '../../../../../../utils';
|
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
||||||
|
import { UploadFileComponent } from '@iqser/common-ui';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-pkcs-signature-configuration',
|
selector: 'redaction-pkcs-signature-configuration',
|
||||||
templateUrl: './pkcs-signature-configuration.component.html',
|
templateUrl: './pkcs-signature-configuration.component.html',
|
||||||
styleUrls: ['./pkcs-signature-configuration.component.scss'],
|
styleUrls: ['./pkcs-signature-configuration.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
|
||||||
})
|
})
|
||||||
export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
||||||
@Input() digitalSignature!: IPkcsDigitalSignatureRequest;
|
@Input() digitalSignature!: IPkcsDigitalSignatureRequest;
|
||||||
|
|
||||||
constructor(protected readonly _digitalSignatureService: DigitalSignatureService, private readonly _formBuilder: UntypedFormBuilder) {
|
constructor(
|
||||||
|
protected readonly _digitalSignatureService: DigitalSignatureService,
|
||||||
|
private readonly _formBuilder: UntypedFormBuilder,
|
||||||
|
) {
|
||||||
super(_digitalSignatureService, DigitalSignatureOptions.PKCS);
|
super(_digitalSignatureService, DigitalSignatureOptions.PKCS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
*ngIf="!form.get('color').value || form.get('color').value?.length === 0"
|
*ngIf="!form.get('color').value || form.get('color').value?.length === 0"
|
||||||
svgIcon="red:color-picker"
|
svgIcon="iqser:color-picker"
|
||||||
></mat-icon>
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { DefaultColorType } from '@red/domain';
|
import { DefaultColorType } from '@red/domain';
|
||||||
import { BaseDialogComponent, IconButtonTypes } from '@iqser/common-ui';
|
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
||||||
import { UntypedFormGroup, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ColorPickerModule } from 'ngx-color-picker';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
|
|
||||||
interface IEditColorData {
|
interface IEditColorData {
|
||||||
colorKey: DefaultColorType;
|
colorKey: DefaultColorType;
|
||||||
@ -16,6 +20,7 @@ interface IEditColorData {
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './edit-color-dialog.component.html',
|
templateUrl: './edit-color-dialog.component.html',
|
||||||
styleUrls: ['./edit-color-dialog.component.scss'],
|
styleUrls: ['./edit-color-dialog.component.scss'],
|
||||||
|
imports: [ReactiveFormsModule, TranslateModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent],
|
||||||
})
|
})
|
||||||
export class EditColorDialogComponent extends BaseDialogComponent {
|
export class EditColorDialogComponent extends BaseDialogComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { UntypedFormGroup, Validators } from '@angular/forms';
|
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { getCurrentUser } from '@users/user.service';
|
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
||||||
import { ISmtpConfiguration } from '@red/domain';
|
import { ISmtpConfiguration } from '@red/domain';
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
import { getCurrentUser } from '@users/user.service';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-smtp-auth-dialog',
|
selector: 'redaction-smtp-auth-dialog',
|
||||||
templateUrl: './smtp-auth-dialog.component.html',
|
templateUrl: './smtp-auth-dialog.component.html',
|
||||||
|
imports: [ReactiveFormsModule, TranslateModule, IconButtonComponent, CircleButtonComponent, MatDialogClose],
|
||||||
})
|
})
|
||||||
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
||||||
readonly #currentUser = getCurrentUser();
|
readonly #currentUser = getCurrentUser();
|
||||||
@ -25,7 +27,7 @@ export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
|||||||
this._dialogRef.close(this.form.getRawValue());
|
this._dialogRef.close(this.form.getRawValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): UntypedFormGroup {
|
private _getForm() {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
user: [this.data?.user || this.#currentUser.email, [Validators.required]],
|
user: [this.data?.user || this.#currentUser.email, [Validators.required]],
|
||||||
password: [this.data?.password, Validators.required],
|
password: [this.data?.password, Validators.required],
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { IconButtonTypes } from '@iqser/common-ui';
|
import { IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './upload-dictionary-dialog.component.html',
|
templateUrl: './upload-dictionary-dialog.component.html',
|
||||||
styleUrls: ['./upload-dictionary-dialog.component.scss'],
|
styleUrls: ['./upload-dictionary-dialog.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [IconButtonComponent, TranslateModule],
|
||||||
})
|
})
|
||||||
export class UploadDictionaryDialogComponent {
|
export class UploadDictionaryDialogComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|
||||||
constructor(private readonly _translateService: TranslateService, public dialogRef: MatDialogRef<UploadDictionaryDialogComponent>) {}
|
constructor(public dialogRef: MatDialogRef<UploadDictionaryDialogComponent>) {}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
|
|||||||
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
<ng-template #headerTemplate>
|
<ng-template #headerTemplate>
|
||||||
<div class="table-header-actions">
|
<div class="table-header-actions">
|
||||||
<redaction-pagination
|
<iqser-pagination
|
||||||
(pageChanged)="pageChanged($event)"
|
(pageChanged)="pageChanged($event)"
|
||||||
[settings]="{ currentPage: logs?.page || 0, totalPages: totalPages }"
|
[settings]="{ currentPage: logs?.page || 0, totalPages: totalPages }"
|
||||||
class="mr-0"
|
class="mr-0"
|
||||||
></redaction-pagination>
|
></iqser-pagination>
|
||||||
|
|
||||||
<div class="separator">·</div>
|
<div class="separator">·</div>
|
||||||
|
|
||||||
<form [formGroup]="form" [attr.help-mode-key]="'audit_filter_audit_entries'">
|
<form [attr.help-mode-key]="'audit_filter_audit_entries'" [formGroup]="form">
|
||||||
<div class="iqser-input-group w-150 mr-20">
|
<div class="iqser-input-group w-150 mr-20">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select (selectionChange)="filterChange()" formControlName="category" id="select-category">
|
<mat-select (selectionChange)="filterChange()" formControlName="category" id="select-category">
|
||||||
@ -96,6 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<!--TODO: use ngTemplateContextGuard for typings here-->
|
||||||
<ng-template #tableItemTemplate let-entity="entity">
|
<ng-template #tableItemTemplate let-entity="entity">
|
||||||
<div *ngIf="cast(entity) as log">
|
<div *ngIf="cast(entity) as log">
|
||||||
<div class="cell">
|
<div class="cell">
|
||||||
@ -116,8 +117,8 @@
|
|||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="openAuditDetails(log)"
|
(action)="openAuditDetails(log)"
|
||||||
[attr.help-mode-key]="'audit_detailed_audit_info'"
|
|
||||||
*ngIf="log.hasDetails"
|
*ngIf="log.hasDetails"
|
||||||
|
[attr.help-mode-key]="'audit_detailed_audit_info'"
|
||||||
[tooltip]="'audit-screen.action.info' | translate"
|
[tooltip]="'audit-screen.action.info' | translate"
|
||||||
icon="red:info"
|
icon="red:info"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|||||||
@ -23,9 +23,3 @@ form {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-info {
|
|
||||||
background: var(--iqser-light);
|
|
||||||
border: 1px solid var(--iqser-grey-1);
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +1,34 @@
|
|||||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { IqserPermissionsService, ListingComponent, listingProvidersFactory, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
import {
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
CircleButtonComponent,
|
||||||
|
HumanizePipe,
|
||||||
|
IqserListingModule,
|
||||||
|
IqserPermissionsService,
|
||||||
|
ListingComponent,
|
||||||
|
listingProvidersFactory,
|
||||||
|
LoadingService,
|
||||||
|
TableColumnConfig,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
|
import { getCurrentUser, InitialsAvatarComponent } from '@iqser/common-ui/lib/users';
|
||||||
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest, User } from '@red/domain';
|
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest, User } from '@red/domain';
|
||||||
import { RouterHistoryService } from '@services/router-history.service';
|
import { RouterHistoryService } from '@services/router-history.service';
|
||||||
import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
|
import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||||
import { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
import { AuditService } from '../../services/audit.service';
|
import { AuditService } from '../../services/audit.service';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { PaginationComponent } from '@common-ui/pagination';
|
||||||
|
import { MatFormField, MatSuffix } from '@angular/material/form-field';
|
||||||
|
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
||||||
|
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
|
|
||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
|
|
||||||
@ -19,6 +36,26 @@ const PAGE_SIZE = 50;
|
|||||||
templateUrl: './audit-screen.component.html',
|
templateUrl: './audit-screen.component.html',
|
||||||
styleUrls: ['./audit-screen.component.scss'],
|
styleUrls: ['./audit-screen.component.scss'],
|
||||||
providers: listingProvidersFactory(AuditScreenComponent),
|
providers: listingProvidersFactory(AuditScreenComponent),
|
||||||
|
imports: [
|
||||||
|
IqserListingModule,
|
||||||
|
TranslateModule,
|
||||||
|
PaginationComponent,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatFormField,
|
||||||
|
MatOption,
|
||||||
|
NgForOf,
|
||||||
|
InitialsAvatarComponent,
|
||||||
|
NgIf,
|
||||||
|
MatIcon,
|
||||||
|
MatSuffix,
|
||||||
|
AsyncPipe,
|
||||||
|
DatePipe,
|
||||||
|
HumanizePipe,
|
||||||
|
CircleButtonComponent,
|
||||||
|
MatSelectTrigger,
|
||||||
|
MatSelect,
|
||||||
|
MatDatepickerModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
|
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
|
||||||
private _previousFrom: Dayjs;
|
private _previousFrom: Dayjs;
|
||||||
@ -26,7 +63,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
readonly ALL_CATEGORIES = 'allCategories';
|
readonly ALL_CATEGORIES = 'allCategories';
|
||||||
readonly ALL_USERS = _('audit-screen.all-users');
|
readonly ALL_USERS = _('audit-screen.all-users');
|
||||||
readonly translations = auditCategoriesTranslations;
|
readonly translations = auditCategoriesTranslations;
|
||||||
readonly form: UntypedFormGroup = this._getForm();
|
readonly form: UntypedFormGroup = this.#getForm();
|
||||||
readonly routerHistoryService = inject(RouterHistoryService);
|
readonly routerHistoryService = inject(RouterHistoryService);
|
||||||
readonly permissionsService = inject(IqserPermissionsService);
|
readonly permissionsService = inject(IqserPermissionsService);
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
@ -59,17 +96,17 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
}
|
}
|
||||||
|
|
||||||
async pageChanged(page: number) {
|
async pageChanged(page: number) {
|
||||||
await this._fetchData(page);
|
await this.#fetchData(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.sortingService.setSortingOption({ column: 'recordDate', order: 'desc' });
|
this.sortingService.setSortingOption({ column: 'recordDate', order: 'desc' });
|
||||||
await this._fetchData();
|
await this.#fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterChange() {
|
async filterChange() {
|
||||||
if (!this._updateDateFilters(this.form.value)) {
|
if (!this.#updateDateFilters(this.form.value)) {
|
||||||
await this._fetchData();
|
await this.#fetchData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +114,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
this._dialogService.openDialog('auditInfo', { auditEntry: log });
|
this._dialogService.openDialog('auditInfo', { auditEntry: log });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getForm(): UntypedFormGroup {
|
#getForm(): UntypedFormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
category: [this.ALL_CATEGORIES],
|
category: [this.ALL_CATEGORIES],
|
||||||
userId: [this.ALL_USERS],
|
userId: [this.ALL_USERS],
|
||||||
@ -86,7 +123,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateDateFilters(value): boolean {
|
#updateDateFilters(value): boolean {
|
||||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -96,16 +133,14 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchData(page?: number) {
|
async #fetchData(page?: number) {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const category = this.form.get('category').value;
|
const category = this.form.get('category').value;
|
||||||
const userId = this.form.get('userId').value;
|
const userId = this.form.get('userId').value;
|
||||||
const from = this.form.get('from').value;
|
const from = this.form.get('from').value ? dayjs(this.form.get('from').value).startOf('day').toISOString() : null;
|
||||||
let to = this.form.get('to').value;
|
const to = this.form.get('to').value ? dayjs(this.form.get('to').value).endOf('day').toISOString() : null;
|
||||||
if (to) {
|
|
||||||
to = to.clone().add(1, 'd');
|
|
||||||
}
|
|
||||||
const logsRequestBody: IAuditSearchRequest = {
|
const logsRequestBody: IAuditSearchRequest = {
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
page: page,
|
page: page,
|
||||||
|
|||||||
@ -0,0 +1,130 @@
|
|||||||
|
@if (componentDefinitions$ | async; as componentDefinitions) {
|
||||||
|
<div class="content-container">
|
||||||
|
<div class="content-header">
|
||||||
|
<div class="header-title">
|
||||||
|
<span
|
||||||
|
class="all-caps-label"
|
||||||
|
[innerHTML]="'component-definitions.title' | translate: { length: componentDefinitions.length }"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
@if (permissionsService.canEditEntities()) {
|
||||||
|
<iqser-icon-button
|
||||||
|
(action)="createEmptyComponentDefinition()"
|
||||||
|
[label]="'component-definitions.add-new' | translate"
|
||||||
|
[type]="iconButtonTypes.primary"
|
||||||
|
icon="iqser:plus"
|
||||||
|
></iqser-icon-button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="components-list">
|
||||||
|
<div class="header">
|
||||||
|
<div class="item-content">
|
||||||
|
<div class="all-caps-label">{{ 'component-definitions.columns.position' | translate }}</div>
|
||||||
|
<div class="all-caps-label">{{ 'component-definitions.columns.name' | translate }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div (cdkDropListDropped)="drop($event)" cdkDropList class="list-content">
|
||||||
|
@for (component of componentDefinitions; track component) {
|
||||||
|
<div
|
||||||
|
class="list-item"
|
||||||
|
[class.selected]="selectedComponent?.id === component.id"
|
||||||
|
cdkDrag
|
||||||
|
(click)="selectComponent(component)"
|
||||||
|
>
|
||||||
|
<div class="item-content">
|
||||||
|
<div class="table-item-title heading">
|
||||||
|
<mat-icon cdkDragHandle class="draggable" svgIcon="red:draggable-dots"></mat-icon>
|
||||||
|
<span> {{ component.rank }} </span>
|
||||||
|
</div>
|
||||||
|
<div class="table-item-title heading">{{ component.displayName }}</div>
|
||||||
|
<div class="right-content">
|
||||||
|
@if (permissionsService.canEditEntities()) {
|
||||||
|
<iqser-circle-button
|
||||||
|
[class]="'delete-component-definition'"
|
||||||
|
(action)="deleteComponent(component.id)"
|
||||||
|
[tooltip]="'trash.action.delete' | translate"
|
||||||
|
icon="iqser:trash"
|
||||||
|
></iqser-circle-button>
|
||||||
|
}
|
||||||
|
<mat-icon
|
||||||
|
[class.not-visible]="selectedComponent?.id !== component.id"
|
||||||
|
class="arrow-right"
|
||||||
|
svgIcon="red:arrow-right"
|
||||||
|
></mat-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (selectedComponent) {
|
||||||
|
<section class="dialog">
|
||||||
|
<div class="dialog-header">
|
||||||
|
@if (selectedComponent.id) {
|
||||||
|
<div
|
||||||
|
class="heading-l"
|
||||||
|
[innerHTML]="'component-definitions.edit-title' | translate: { displayName: selectedComponent.displayName }"
|
||||||
|
></div>
|
||||||
|
} @else {
|
||||||
|
<div class="heading-l" [innerHTML]="'component-definitions.add-title' | translate"></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (form) {
|
||||||
|
<form (submit)="save()" [formGroup]="form">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<div class="iqser-input-group w-300 required">
|
||||||
|
<label [translate]="'component-definitions.form.display-name'"></label>
|
||||||
|
<input
|
||||||
|
formControlName="displayName"
|
||||||
|
name="displayName"
|
||||||
|
placeholder="{{ 'component-definitions.form.display-name-placeholder' | translate }}"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label [translate]="'component-definitions.form.description'"></label>
|
||||||
|
<textarea
|
||||||
|
[placeholder]="'component-definitions.form.description-placeholder' | translate"
|
||||||
|
formControlName="description"
|
||||||
|
iqserHasScrollbar
|
||||||
|
name="description"
|
||||||
|
rows="5"
|
||||||
|
type="text"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="iqser-input-group w-450">
|
||||||
|
<label [translate]="'component-definitions.form.technical-name-label'"></label>
|
||||||
|
<span class="technical-name"> {{ technicalName }} </span>
|
||||||
|
<label [translate]="'component-definitions.form.autogenerated-label'"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<iqser-icon-button
|
||||||
|
[disabled]="disabled"
|
||||||
|
[label]="'component-definitions.actions.save' | translate"
|
||||||
|
[submit]="true"
|
||||||
|
[type]="iconButtonTypes.primary"
|
||||||
|
></iqser-icon-button>
|
||||||
|
@if (selectedComponent.id) {
|
||||||
|
<div
|
||||||
|
(click)="initForm()"
|
||||||
|
[class.disabled]="disabled"
|
||||||
|
[translate]="'component-definitions.actions.revert'"
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user