mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-03 18:58:24 +00:00 
			
		
		
		
	Compare commits
	
		
			316 Commits
		
	
	
		
			451-activi
			...
			ticket/64-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						3f6bbbc1b3
	
				 | 
					
					
						|||
| 
						
						
							
						
						31e57d7507
	
				 | 
					
					
						|||
| 
						
						
							
						
						5c098a336d
	
				 | 
					
					
						|||
| 
						
						
							
						
						e107d20bea
	
				 | 
					
					
						|||
| 
						
						
							
						
						491fd81f9b
	
				 | 
					
					
						|||
| 
						
						
							
						
						8c2acbd166
	
				 | 
					
					
						|||
| 
						
						
							
						
						e291c7abec
	
				 | 
					
					
						|||
| 
						
						
							
						
						1e186fab58
	
				 | 
					
					
						|||
| 
						
						
							
						
						83a2c04537
	
				 | 
					
					
						|||
| 
						
						
							
						
						e89b33bc1a
	
				 | 
					
					
						|||
| 
						
						
							
						
						6b208e9962
	
				 | 
					
					
						|||
| 
						
						
							
						
						b0c63fab91
	
				 | 
					
					
						|||
| 
						
						
							
						
						6d4c4d2c74
	
				 | 
					
					
						|||
| 
						
						
							
						
						df6087d468
	
				 | 
					
					
						|||
| 
						
						
							
						
						ffac143ab9
	
				 | 
					
					
						|||
| 
						
						
							
						
						f1bf6023ff
	
				 | 
					
					
						|||
| 
						
						
							
						
						71e146e4f0
	
				 | 
					
					
						|||
| 
						
						
							
						
						4234377b7e
	
				 | 
					
					
						|||
| 
						
						
							
						
						1be82b3049
	
				 | 
					
					
						|||
| 
						
						
							
						
						808954df42
	
				 | 
					
					
						|||
| 
						
						
							
						
						3f8bb6c5c0
	
				 | 
					
					
						|||
| 
						
						
							
						
						23e1a0d36a
	
				 | 
					
					
						|||
| 
						
						
							
						
						a594d86346
	
				 | 
					
					
						|||
| 
						
						
							
						
						870907804b
	
				 | 
					
					
						|||
| 
						
						
							
						
						e9e6c05e3d
	
				 | 
					
					
						|||
| 
						
						
							
						
						532f2dd842
	
				 | 
					
					
						|||
| 
						
						
							
						
						d14d4d4d8f
	
				 | 
					
					
						|||
| a22cbe0239 | |||
| 98902bdeb8 | |||
| 
						
						
							
						
						592a0f3698
	
				 | 
					
					
						|||
| 
						
						
							
						
						d469eb19ad
	
				 | 
					
					
						|||
| 4765d4fe28 | |||
| 
						 | 
					30bcb85549 | ||
| 
						
						
							
						
						189a9337b4
	
				 | 
					
					
						|||
| 
						
						
							
						
						c030232a73
	
				 | 
					
					
						|||
| 
						
						
							
						
						d4f9726f90
	
				 | 
					
					
						|||
| 
						
						
							
						
						8740025dbd
	
				 | 
					
					
						|||
| 
						
						
							
						
						6d8ef035ea
	
				 | 
					
					
						|||
| 
						
						
							
						
						60eab628ee
	
				 | 
					
					
						|||
| 
						
						
							
						
						1fd559b722
	
				 | 
					
					
						|||
| 
						
						
							
						
						b526e802d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						60937152c3
	
				 | 
					
					
						|||
| 
						
						
							
						
						6d2e78ce55
	
				 | 
					
					
						|||
| 
						
						
							
						
						e566f60a4a
	
				 | 
					
					
						|||
| 
						
						
							
						
						c06531cddb
	
				 | 
					
					
						|||
| 61ca700bbe | |||
| 
						 | 
					b43aeebc3c | ||
| 056e2dcc5f | |||
| e57d1ac696 | |||
| 
						
						
							
						
						4a1da25fee
	
				 | 
					
					
						|||
| 0eff1d2e79 | |||
| 3928b2cc7a | |||
| 
						
						
							
						
						02783e5391
	
				 | 
					
					
						|||
| 
						
						
							
						
						4f51ef81ad
	
				 | 
					
					
						|||
| 
						
						
							
						
						4637dc692c
	
				 | 
					
					
						|||
| 
						
						
							
						
						be3b9f0f56
	
				 | 
					
					
						|||
| 
						
						
							
						
						ee006f55d6
	
				 | 
					
					
						|||
| 
						
						
							
						
						13b1c45271
	
				 | 
					
					
						|||
| 
						
						
							
						
						ad2b6d63ac
	
				 | 
					
					
						|||
| 
						
						
							
						
						bfbde078b7
	
				 | 
					
					
						|||
| 
						
						
							
						
						d42a1296c4
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b7e3c1601
	
				 | 
					
					
						|||
| 
						
						
							
						
						6ea9af588b
	
				 | 
					
					
						|||
| 
						
						
							
						
						0fd76d3fa8
	
				 | 
					
					
						|||
| 
						
						
							
						
						34af53130b
	
				 | 
					
					
						|||
| 
						
						
							
						
						a1fd395868
	
				 | 
					
					
						|||
| 
						
						
							
						
						b8a7cbb321
	
				 | 
					
					
						|||
| 
						
						
							
						
						6124eb9e34
	
				 | 
					
					
						|||
| 
						
						
							
						
						a5b06de92a
	
				 | 
					
					
						|||
| 
						
						
							
						
						52404956d2
	
				 | 
					
					
						|||
| 
						
						
							
						
						4207efd6bf
	
				 | 
					
					
						|||
| 
						
						
							
						
						840fde4ad4
	
				 | 
					
					
						|||
| 
						
						
							
						
						3611ea2518
	
				 | 
					
					
						|||
| 
						
						
							
						
						bbd4292cb9
	
				 | 
					
					
						|||
| 
						
						
							
						
						54f8c92240
	
				 | 
					
					
						|||
| 
						
						
							
						
						5330befc8f
	
				 | 
					
					
						|||
| 
						
						
							
						
						c19206be0c
	
				 | 
					
					
						|||
| 
						
						
							
						
						5ff374d2fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						4a73aaae94
	
				 | 
					
					
						|||
| 
						
						
							
						
						ff2c567d05
	
				 | 
					
					
						|||
| 
						
						
							
						
						a734e84f28
	
				 | 
					
					
						|||
| 
						
						
							
						
						4367ed086e
	
				 | 
					
					
						|||
| 
						
						
							
						
						3227bfcd3a
	
				 | 
					
					
						|||
| 
						
						
							
						
						8d29fb260a
	
				 | 
					
					
						|||
| 
						
						
							
						
						bda0743c63
	
				 | 
					
					
						|||
| 
						
						
							
						
						d9b730627f
	
				 | 
					
					
						|||
| 
						
						
							
						
						27548ad654
	
				 | 
					
					
						|||
| 
						
						
							
						
						bec7297039
	
				 | 
					
					
						|||
| 
						
						
							
						
						852523e644
	
				 | 
					
					
						|||
| 
						
						
							
						
						c05d0aad47
	
				 | 
					
					
						|||
| 
						
						
							
						
						1c0ed9abc8
	
				 | 
					
					
						|||
| 
						
						
							
						
						9aed5cc216
	
				 | 
					
					
						|||
| 
						
						
							
						
						e4fe5bff68
	
				 | 
					
					
						|||
| 
						
						
							
						
						4c73c4d9d0
	
				 | 
					
					
						|||
| 38935edb93 | |||
| 
						 | 
					e1ef65d4ca | ||
| ec9d0be70b | |||
| 
						 | 
					0ba2cbc1e8 | ||
| e87429933a | |||
| 
						
						
							
						
						8e2e676e3d
	
				 | 
					
					
						|||
| e12ad563a3 | |||
| 
						 | 
					711aa8db9b | ||
| e78d44953f | |||
| 18f67801c7 | |||
| 
						
						
							
						
						c815e6bc69
	
				 | 
					
					
						|||
| 807f2711fe | |||
| 
						 | 
					cd594cd580 | ||
| 
						
						
							
						
						fb6b26bfb5
	
				 | 
					
					
						|||
| 
						
						
							
						
						c5cedb8bd6
	
				 | 
					
					
						|||
| 
						
						
							
						
						2665e43a61
	
				 | 
					
					
						|||
| 
						
						
							
						
						25561cdf63
	
				 | 
					
					
						|||
| 10b73e06e1 | |||
| 
						 | 
					e7c04e34a9 | ||
| 164beee7c6 | |||
| 
						 | 
					4d96eb9457 | ||
| fe2eba3b29 | |||
| 
						 | 
					61d1232e31 | ||
| 6594d4f6a6 | |||
| 1a66a9e864 | |||
| 1b74c119dc | |||
| 14d88810f3 | |||
| 445a2c9358 | |||
| c8baf0a8aa | |||
| faed443a96 | |||
| bbf387d96f | |||
| 
						 | 
					b7c9b60744 | ||
| 
						
						
							
						
						2bd303bbbe
	
				 | 
					
					
						|||
| 
						
						
							
						
						c5e6122d2c
	
				 | 
					
					
						|||
| 088b876e20 | |||
| 3400656d7c | |||
| 
						
						
							
						
						568c8be7fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						538ecc42ea
	
				 | 
					
					
						|||
| 
						
						
							
						
						15d26d4b06
	
				 | 
					
					
						|||
| 
						
						
							
						
						d8bd9bd7cd
	
				 | 
					
					
						|||
| 
						
						
							
						
						dcdfba5ccd
	
				 | 
					
					
						|||
| 
						
						
							
						
						0204bdd38d
	
				 | 
					
					
						|||
| 
						
						
							
						
						392fd01b56
	
				 | 
					
					
						|||
| 35844f3b73 | |||
| 7506b918d7 | |||
| cfba291f2c | |||
| 
						 | 
					04438c09d3 | ||
| 2a54d1b909 | |||
| 
						 | 
					628eeac5e0 | ||
| a2263b3fa1 | |||
| 74796d0fb0 | |||
| 
						
						
							
						
						c19481e40a
	
				 | 
					
					
						|||
| 
						 | 
					6eeb717b1a | ||
| 
						
						
							
						
						beb7c462da
	
				 | 
					
					
						|||
| 
						 | 
					dbf363a9e8 | ||
| 
						
						
							
						
						64a2f7c9ed
	
				 | 
					
					
						|||
| f26d9739c8 | |||
| 
						
						
							
						
						afa5edc1d8
	
				 | 
					
					
						|||
| 
						
						
							
						
						42d6c9e672
	
				 | 
					
					
						|||
| 
						
						
							
						
						2b22d4cb7c
	
				 | 
					
					
						|||
| 
						
						
							
						
						c8e5d0eb37
	
				 | 
					
					
						|||
| 2bf8ad5d6c | |||
| 11698a52e3 | |||
| 70955573e8 | |||
| 
						 | 
					3df4043eb9 | ||
| 
						
						
							
						
						06e8264dde
	
				 | 
					
					
						|||
| b451d2c4a3 | |||
| 4f93150874 | |||
| 0566ab0910 | |||
| 
						 | 
					f4eeee1598 | ||
| 33cf16fc13 | |||
| 0a331aab37 | |||
| d43b739654 | |||
| c72432efae | |||
| 
						
						
							
						
						95975fae55
	
				 | 
					
					
						|||
| 
						
						
							
						
						95a7efa138
	
				 | 
					
					
						|||
| 
						
						
							
						
						45e193ff6d
	
				 | 
					
					
						|||
| 
						
						
							
						
						dfc146ff3f
	
				 | 
					
					
						|||
| b41fcf66a9 | |||
| 
						 | 
					a8dd1b3548 | ||
| 
						
						
							
						
						2b99a480ac
	
				 | 
					
					
						|||
| 
						
						
							
						
						7633e587bb
	
				 | 
					
					
						|||
| 
						
						
							
						
						fc61dfdf3a
	
				 | 
					
					
						|||
| 
						
						
							
						
						f1a5b5c49e
	
				 | 
					
					
						|||
| 
						
						
							
						
						ec685dcd47
	
				 | 
					
					
						|||
| 
						
						
							
						
						631ae3eedd
	
				 | 
					
					
						|||
| 
						
						
							
						
						440a7837ac
	
				 | 
					
					
						|||
| 
						
						
							
						
						e0abf34784
	
				 | 
					
					
						|||
| 
						
						
							
						
						377ae9a9dc
	
				 | 
					
					
						|||
| 
						
						
							
						
						034dc30e30
	
				 | 
					
					
						|||
| 
						
						
							
						
						d615111a0f
	
				 | 
					
					
						|||
| 
						
						
							
						
						ffb756c712
	
				 | 
					
					
						|||
| 
						
						
							
						
						69daccb860
	
				 | 
					
					
						|||
| 
						
						
							
						
						16435423cf
	
				 | 
					
					
						|||
| 
						
						
							
						
						697b4ab436
	
				 | 
					
					
						|||
| 
						
						
							
						
						67d804e28e
	
				 | 
					
					
						|||
| 
						
						
							
						
						cf41fa9574
	
				 | 
					
					
						|||
| 
						
						
							
						
						b8b325f7d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						e97bd8c4ef
	
				 | 
					
					
						|||
| 
						
						
							
						
						e28d7df533
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b20b1bc01
	
				 | 
					
					
						|||
| 
						
						
							
						
						b15733076c
	
				 | 
					
					
						|||
| 
						
						
							
						
						25be5c9ea3
	
				 | 
					
					
						|||
| 
						
						
							
						
						b035020c6f
	
				 | 
					
					
						|||
| 
						
						
							
						
						128101dc46
	
				 | 
					
					
						|||
| 
						
						
							
						
						5f2711023e
	
				 | 
					
					
						|||
| 
						
						
							
						
						bdf2ed4bbd
	
				 | 
					
					
						|||
| 
						
						
							
						
						1df542603e
	
				 | 
					
					
						|||
| 
						
						
							
						
						80bcc68ce5
	
				 | 
					
					
						|||
| 
						
						
							
						
						154fc3e2f6
	
				 | 
					
					
						|||
| 
						
						
							
						
						e45af94c78
	
				 | 
					
					
						|||
| 
						
						
							
						
						166a6fde20
	
				 | 
					
					
						|||
| 
						
						
							
						
						631f047338
	
				 | 
					
					
						|||
| 
						
						
							
						
						a777588bb8
	
				 | 
					
					
						|||
| 
						
						
							
						
						ca78d112c2
	
				 | 
					
					
						|||
| 
						
						
							
						
						bcfd317d83
	
				 | 
					
					
						|||
| 
						
						
							
						
						348740f073
	
				 | 
					
					
						|||
| 
						
						
							
						
						0d74f0980f
	
				 | 
					
					
						|||
| 
						
						
							
						
						be19dc00db
	
				 | 
					
					
						|||
| 
						
						
							
						
						643028ffd6
	
				 | 
					
					
						|||
| 
						
						
							
						
						ac4e2e5bf2
	
				 | 
					
					
						|||
| 
						
						
							
						
						498572b96e
	
				 | 
					
					
						|||
| 
						
						
							
						
						d2a61ce69b
	
				 | 
					
					
						|||
| 
						
						
							
						
						a9c0567ee1
	
				 | 
					
					
						|||
| 
						
						
							
						
						76cec5b5a8
	
				 | 
					
					
						|||
| 
						
						
							
						
						efe8a67697
	
				 | 
					
					
						|||
| 
						
						
							
						
						26dfa9b028
	
				 | 
					
					
						|||
| 
						
						
							
						
						50025044d3
	
				 | 
					
					
						|||
| 
						
						
							
						
						e6202a2e34
	
				 | 
					
					
						|||
| 
						
						
							
						
						b863bd967d
	
				 | 
					
					
						|||
| 
						
						
							
						
						e65bcf7275
	
				 | 
					
					
						|||
| 
						
						
							
						
						e00ece4200
	
				 | 
					
					
						|||
| 
						
						
							
						
						640fd71402
	
				 | 
					
					
						|||
| 
						
						
							
						
						aae50ca290
	
				 | 
					
					
						|||
| 
						
						
							
						
						1fa483598b
	
				 | 
					
					
						|||
| 
						
						
							
						
						e4b6a468f8
	
				 | 
					
					
						|||
| 
						 | 
					66c7758023 | ||
| 
						 | 
					4750d2c24e | ||
| 
						 | 
					ca05e3d979 | ||
| 
						 | 
					a20f9b4f86 | ||
| 
						 | 
					c73c1eb8d5 | ||
| 
						 | 
					8778bb0731 | ||
| 
						 | 
					c7d20eebc5 | ||
| 
						 | 
					b9e130c159 | ||
| 
						 | 
					3e8bc94af3 | ||
| 
						 | 
					0c914c9f9f | ||
| 
						 | 
					580a60c939 | ||
| 
						 | 
					4996ac3b7c | ||
| 
						 | 
					2a23bf19cb | ||
| 
						 | 
					650d2596d9 | ||
| 
						 | 
					2bdd5a329e | ||
| 
						
						
							
						
						78d1776733
	
				 | 
					
					
						|||
| 
						
						
							
						
						66dc603c85
	
				 | 
					
					
						|||
| 
						
						
							
						
						3a8154ecce
	
				 | 
					
					
						|||
| 
						
						
							
						
						c81828e04f
	
				 | 
					
					
						|||
| 
						 | 
					ec17dd7de2 | ||
| 76c076a5f3 | |||
| 
						 | 
					f0045edd6c | ||
| 
						 | 
					d00b76ffcd | ||
| 
						 | 
					8991f0ef3f | ||
| 
						 | 
					d6f5eae0c9 | ||
| 
						 | 
					821fce3dd8 | ||
| 
						 | 
					1d33ae1e39 | ||
| 
						 | 
					19af0feb57 | ||
| 
						 | 
					1c09e9a692 | ||
| 
						 | 
					d72e748388 | ||
| 
						 | 
					ab850b7b70 | ||
| 
						 | 
					3f9745d8cf | ||
| 
						 | 
					473765366a | ||
| 
						 | 
					6500c24a7f | ||
| 
						 | 
					1d00457141 | ||
| 
						 | 
					eb0bf56cff | ||
| 
						 | 
					7b8cd90cf1 | ||
| 
						 | 
					a27d92aba0 | ||
| 
						 | 
					85bdfb9e21 | ||
| 
						 | 
					4cffcf4de1 | ||
| 
						 | 
					b2587a688f | ||
| 
						 | 
					c9f0e9843b | ||
| 
						 | 
					b40ad9e445 | ||
| 
						 | 
					3e10e47e29 | ||
| 
						 | 
					2a1963e993 | ||
| 34c171659b | |||
| 
						
						
							
						
						2d8b960d9e
	
				 | 
					
					
						|||
| 831ae03431 | |||
| 
						
						
							
						
						45828174d1
	
				 | 
					
					
						|||
| 
						
						
							
						
						ed45f14a45
	
				 | 
					
					
						|||
| 
						
						
							
						
						fa67835690
	
				 | 
					
					
						|||
| 
						
						
							
						
						b434d38091
	
				 | 
					
					
						|||
| 
						 | 
					800a952532 | ||
| 
						
						
							
						
						9f355032a8
	
				 | 
					
					
						|||
| 
						
						
							
						
						0bc6e62d4d
	
				 | 
					
					
						|||
| 
						
						
							
						
						46fb1c04b5
	
				 | 
					
					
						|||
| 3b2c3d1464 | |||
| 
						 | 
					0bd6038160 | ||
| 
						 | 
					baab8e94ce | ||
| 
						
						
							
						
						e2deb55fdb
	
				 | 
					
					
						|||
| 
						 | 
					2cdfb50058 | ||
| 
						
						
							
						
						39d701feb2
	
				 | 
					
					
						|||
| 
						
						
							
						
						613ee8b186
	
				 | 
					
					
						|||
| 
						
						
							
						
						56a1a488de
	
				 | 
					
					
						|||
| 3f789ad0f4 | |||
| 
						
						
							
						
						467bea7cde
	
				 | 
					
					
						|||
| 
						
						
							
						
						670b8eb82b
	
				 | 
					
					
						|||
| 
						
						
							
						
						a9760b323f
	
				 | 
					
					
						|||
| 
						
						
							
						
						71a3a1924a
	
				 | 
					
					
						|||
| 
						
						
							
						
						ecdc1e25bf
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd37427be1
	
				 | 
					
					
						|||
| 
						
						
							
						
						c8467df1b1
	
				 | 
					
					
						|||
| 
						
						
							
						
						4c89a954fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						7c1f3b114d
	
				 | 
					
					
						|||
| 
						
						
							
						
						36bc4dab24
	
				 | 
					
					
						|||
| 
						
						
							
						
						4b30d92282
	
				 | 
					
					
						|||
| 
						
						
							
						
						75fbec5489
	
				 | 
					
					
						|||
| 
						
						
							
						
						912fdd6349
	
				 | 
					
					
						|||
| 
						
						
							
						
						5832542978
	
				 | 
					
					
						|||
| 
						
						
							
						
						5c3585a1ed
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2f1e20ddf
	
				 | 
					
					
						|||
| 
						
						
							
						
						4d67702a76
	
				 | 
					
					
						|||
| 18e442db29 | |||
| 
						 | 
					deb3d92189 | ||
| 
						
						
							
						
						a59ea7db31
	
				 | 
					
					
						|||
| 
						
						
							
						
						a738b0cac9
	
				 | 
					
					
						
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20240530-160003.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20240530-160003.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					kind: Feature
 | 
				
			||||||
 | 
					body: |
 | 
				
			||||||
 | 
					  Upgrade import of address list to the last version of compiled addresses of belgian-best-address
 | 
				
			||||||
 | 
					time: 2024-05-30T16:00:03.440767606+02:00
 | 
				
			||||||
 | 
					custom:
 | 
				
			||||||
 | 
					  Issue: ""
 | 
				
			||||||
							
								
								
									
										6
									
								
								.changes/unreleased/Feature-20240531-190242.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.changes/unreleased/Feature-20240531-190242.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					kind: Feature
 | 
				
			||||||
 | 
					body: |
 | 
				
			||||||
 | 
					  Upgrade CKEditor and refactor configuration with use of typescript
 | 
				
			||||||
 | 
					time: 2024-05-31T19:02:42.776662753+02:00
 | 
				
			||||||
 | 
					custom:
 | 
				
			||||||
 | 
					  Issue: ""
 | 
				
			||||||
@@ -19,11 +19,11 @@ max_line_length = 80
 | 
				
			|||||||
[COMMIT_EDITMSG]
 | 
					[COMMIT_EDITMSG]
 | 
				
			||||||
max_line_length = 0
 | 
					max_line_length = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[*.{js, vue, ts}]
 | 
					[*.{js,vue,ts}]
 | 
				
			||||||
indent_size = 2
 | 
					indent_size = 2
 | 
				
			||||||
indent_style = space
 | 
					indent_style = space
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[.rst]
 | 
					[*.rst]
 | 
				
			||||||
ident_size = 3
 | 
					indent_size = 3
 | 
				
			||||||
ident_style = space
 | 
					indent_style = space
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -234,20 +234,16 @@ This must be a decision made by a human, not by an AI. Every AI task must abort
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#### Running Tests
 | 
					#### Running Tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The tests are run from the project's root (not from the bundle's root).
 | 
					The tests are run from the project's root (not from the bundle's root: so, do not change the directory to any bundle directory before running tests).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tests must be run using the `symfony` command:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
# Run all tests
 | 
					 | 
				
			||||||
vendor/bin/phpunit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Run tests for a specific bundle
 | 
					 | 
				
			||||||
vendor/bin/phpunit --testsuite NameBundle
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Run a specific test file
 | 
					# Run a specific test file
 | 
				
			||||||
vendor/bin/phpunit path/to/TestFile.php
 | 
					symfony composer exec phpunit -- path/to/TestFile.php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Run a specific test method
 | 
					# Run a specific test method
 | 
				
			||||||
vendor/bin/phpunit --filter methodName path/to/TestFile.php
 | 
					symfony composer exec phpunit -- --filter methodName path/to/TestFile.php
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Test Structure
 | 
					#### Test Structure
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "tabWidth": 2,
 | 
				
			||||||
 | 
					  "useTabs": false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Use IntelliSense to learn about possible attributes.
 | 
				
			||||||
 | 
					    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
 | 
				
			||||||
 | 
					    "version": "0.2.0",
 | 
				
			||||||
 | 
					    "configurations": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Chill Debug",
 | 
				
			||||||
 | 
					            "type": "php",
 | 
				
			||||||
 | 
					            "request": "launch",
 | 
				
			||||||
 | 
					            "port": 9000,
 | 
				
			||||||
 | 
					            "pathMappings": {
 | 
				
			||||||
 | 
					                "/var/www/html": "${workspaceFolder}"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "preLaunchTask": "symfony"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Yarn Encore Dev (Watch)",
 | 
				
			||||||
 | 
					            "type": "node-terminal",
 | 
				
			||||||
 | 
					            "request": "launch",
 | 
				
			||||||
 | 
					            "command": "yarn encore dev --watch",
 | 
				
			||||||
 | 
					            "cwd": "${workspaceFolder}"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "compounds": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": "Chill Debug + Yarn Encore Dev (Watch)",
 | 
				
			||||||
 | 
					            "configurations": ["Chill Debug", "Yarn Encore Dev (Watch)"]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										23
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "tasks": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "type": "shell",
 | 
				
			||||||
 | 
					            "command": "symfony",
 | 
				
			||||||
 | 
					            "args": [
 | 
				
			||||||
 | 
					                "server:start",
 | 
				
			||||||
 | 
					                "--allow-http",
 | 
				
			||||||
 | 
					                "--no-tls",
 | 
				
			||||||
 | 
					                "--port=8000",
 | 
				
			||||||
 | 
					                "--allow-all-ip",
 | 
				
			||||||
 | 
					                "-d"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "label": "symfony"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "type": "shell",
 | 
				
			||||||
 | 
					            "command": "yarn",
 | 
				
			||||||
 | 
					            "args": ["encore", "dev", "--watch"],
 | 
				
			||||||
 | 
					            "label": "webpack"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -54,7 +54,7 @@ Arborescence:
 | 
				
			|||||||
    - person
 | 
					    - person
 | 
				
			||||||
    - personvendee
 | 
					    - personvendee
 | 
				
			||||||
    - household_edit_metadata
 | 
					    - household_edit_metadata
 | 
				
			||||||
        - index.js
 | 
					        - index.ts
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Organisation des feuilles de styles
 | 
					## Organisation des feuilles de styles
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,12 @@
 | 
				
			|||||||
import { trans, setLocale, setLocaleFallbacks } from "./ux-translator";
 | 
					import {
 | 
				
			||||||
 | 
					  trans,
 | 
				
			||||||
 | 
					  setLocale,
 | 
				
			||||||
 | 
					  getLocale,
 | 
				
			||||||
 | 
					  setLocaleFallbacks,
 | 
				
			||||||
 | 
					} from "./ux-translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setLocaleFallbacks({"en": "fr", "nl": "fr", "fr": "en"});
 | 
					setLocaleFallbacks({ en: "fr", nl: "fr", fr: "en" });
 | 
				
			||||||
setLocale('fr');
 | 
					setLocale("fr");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { trans };
 | 
					export { trans, getLocale };
 | 
				
			||||||
export * from '../var/translations';
 | 
					export * from "../var/translations";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,6 +133,7 @@
 | 
				
			|||||||
            "Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
 | 
					            "Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
 | 
				
			||||||
            "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
 | 
					            "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
 | 
				
			||||||
            "Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
 | 
					            "Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
 | 
				
			||||||
 | 
					            "Chill\\TicketBundle\\": "src/Bundle/ChillTicketBundle/src",
 | 
				
			||||||
            "Chill\\Utils\\Rector\\": "utils/rector/src"
 | 
					            "Chill\\Utils\\Rector\\": "utils/rector/src"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@ return [
 | 
				
			|||||||
    Chill\ThirdPartyBundle\ChillThirdPartyBundle::class => ['all' => true],
 | 
					    Chill\ThirdPartyBundle\ChillThirdPartyBundle::class => ['all' => true],
 | 
				
			||||||
    Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true],
 | 
					    Chill\BudgetBundle\ChillBudgetBundle::class => ['all' => true],
 | 
				
			||||||
    Chill\WopiBundle\ChillWopiBundle::class => ['all' => true],
 | 
					    Chill\WopiBundle\ChillWopiBundle::class => ['all' => true],
 | 
				
			||||||
 | 
					    Chill\TicketBundle\ChillTicketBundle::class => ['all' => true],
 | 
				
			||||||
    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
 | 
					    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
 | 
				
			||||||
    Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
 | 
					    Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
chill_doc_store:
 | 
					chill_doc_store:
 | 
				
			||||||
    use_driver: openstack
 | 
					    use_driver: local_storage
 | 
				
			||||||
    local_storage:
 | 
					    local_storage:
 | 
				
			||||||
        storage_path: '%kernel.project_dir%/var/storage'
 | 
					        storage_path: '%kernel.project_dir%/var/storage'
 | 
				
			||||||
    openstack:
 | 
					    openstack:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								config/packages/chill_ticket.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								config/packages/chill_ticket.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					chill_ticket:
 | 
				
			||||||
 | 
					    ticket:
 | 
				
			||||||
 | 
					        person_per_ticket:    one # One of "one"; "many"
 | 
				
			||||||
 | 
					        response_time_exceeded_delay: PT12H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,6 +14,7 @@ doctrine_migrations:
 | 
				
			|||||||
        'Chill\Migrations\Calendar': '@ChillCalendarBundle/migrations'
 | 
					        'Chill\Migrations\Calendar': '@ChillCalendarBundle/migrations'
 | 
				
			||||||
        'Chill\Migrations\Budget': '@ChillBudgetBundle/migrations'
 | 
					        'Chill\Migrations\Budget': '@ChillBudgetBundle/migrations'
 | 
				
			||||||
        'Chill\Migrations\Report': '@ChillReportBundle/migrations'
 | 
					        'Chill\Migrations\Report': '@ChillReportBundle/migrations'
 | 
				
			||||||
 | 
					        'Chill\Migrations\Ticket': '@ChillTicketBundle/migrations'
 | 
				
			||||||
    all_or_nothing:
 | 
					    all_or_nothing:
 | 
				
			||||||
        true
 | 
					        true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,7 @@ framework:
 | 
				
			|||||||
            'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority
 | 
					            'Chill\MainBundle\Export\Messenger\ExportRequestGenerationMessage': priority
 | 
				
			||||||
            'Chill\MainBundle\Export\Messenger\RemoveExportGenerationMessage': async
 | 
					            'Chill\MainBundle\Export\Messenger\RemoveExportGenerationMessage': async
 | 
				
			||||||
            'Chill\MainBundle\Notification\Email\NotificationEmailMessages\ScheduleDailyNotificationDigestMessage': async
 | 
					            'Chill\MainBundle\Notification\Email\NotificationEmailMessages\ScheduleDailyNotificationDigestMessage': async
 | 
				
			||||||
 | 
					            'Chill\TicketBundle\Messenger\PostTicketUpdateMessage': async
 | 
				
			||||||
            # end of routes added by chill-bundles recipes
 | 
					            # end of routes added by chill-bundles recipes
 | 
				
			||||||
            # Route your messages to the transports
 | 
					            # Route your messages to the transports
 | 
				
			||||||
            # 'App\Message\YourMessage': async
 | 
					            # 'App\Message\YourMessage': async
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								config/routes/chill_ticket.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								config/routes/chill_ticket.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					chill_ticket_bundle:
 | 
				
			||||||
 | 
					    resource: '@ChillTicketBundle/config/routes.yaml'
 | 
				
			||||||
@@ -11,24 +11,94 @@
 | 
				
			|||||||
Create a new bundle
 | 
					Create a new bundle
 | 
				
			||||||
*******************
 | 
					*******************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Create your own bundle is not a trivial task.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The easiest way to achieve this is seems to be : 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1. Prepare a fresh installation of the chill project, in a new directory
 | 
					 | 
				
			||||||
2. Create a new bundle in this project, in the src directory
 | 
					 | 
				
			||||||
3. Initialize a git repository **at the root bundle**, and create your initial commit.
 | 
					 | 
				
			||||||
4. Register the bundle with composer/packagist. If you do not plan to distribute your bundle with packagist, you may use a custom repository for achieve this [#f1]_
 | 
					 | 
				
			||||||
5. Move to a development installation, made as described in the :ref:`installation-for-development` section, and add your new repository to the composer.json file
 | 
					 | 
				
			||||||
6. Work as :ref:`usual <editing-code-and-commiting>`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. warning::
 | 
					.. warning::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    This part of the doc is not yet tested
 | 
					    This part of the doc is not yet tested
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO
 | 
					Create a new directory with Bundle class
 | 
				
			||||||
 | 
					----------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   mkdir -p src/Bundle/ChillSomeBundle/src/config
 | 
				
			||||||
 | 
					   mkdir -p src/Bundle/ChillSomeBundle/src/Controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add a bundle file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Chill is a software for social workers
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * For the full copyright and license information, please view
 | 
				
			||||||
 | 
					     * the LICENSE file that was distributed with this source code.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    namespace Chill\SomeBundle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use Symfony\Component\HttpKernel\Bundle\Bundle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ChillSomeBundle extends Bundle {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And a route file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   chill_ticket_controller:
 | 
				
			||||||
 | 
					       resource: '@ChillTicketBundle/Controller/'
 | 
				
			||||||
 | 
					       type: annotation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Register the new psr-4 namespace
 | 
				
			||||||
 | 
					--------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In composer.json, add the new psr4 namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    "autoload": {
 | 
				
			||||||
 | 
					        "psr-4": {
 | 
				
			||||||
 | 
					    +        "Chill\\SomeBundle\\": "src/Bundle/ChillSomeBundle/src",
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. rubric:: Footnotes
 | 
					Register the bundle
 | 
				
			||||||
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Register in the file :code:`config/bundles.php`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Vendor\Bundle\YourBundle\YourBundle::class => ['all' => true],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And import routes in :code:`config/routes/chill_some_bundle.yaml`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   chill_ticket_bundle:
 | 
				
			||||||
 | 
					       resource: '@ChillSomeBundle/config/routes.yaml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add the doctrine_migration namespace
 | 
				
			||||||
 | 
					------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add the namespace to :code:`config/packages/doctrine_migrations_chill.yaml`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   doctrine_migrations:
 | 
				
			||||||
 | 
					       migrations_paths:
 | 
				
			||||||
 | 
					   +        'Chill\Some\Ticket': '@ChillSomeBundle/migrations'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Dump autoloading
 | 
				
			||||||
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   symfony composer dump-autoload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. [#f1] Be aware that we use the Affero GPL Licence, which ensure that all users must have access to derivative works done with this software.
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,7 @@
 | 
				
			|||||||
    "typescript": "^5.6.3",
 | 
					    "typescript": "^5.6.3",
 | 
				
			||||||
    "typescript-eslint": "^8.13.0",
 | 
					    "typescript-eslint": "^8.13.0",
 | 
				
			||||||
    "vue-loader": "^17.0.0",
 | 
					    "vue-loader": "^17.0.0",
 | 
				
			||||||
 | 
					    "vue-tsc": "^3.1.1",
 | 
				
			||||||
    "webpack": "^5.75.0",
 | 
					    "webpack": "^5.75.0",
 | 
				
			||||||
    "webpack-cli": "^5.0.1"
 | 
					    "webpack-cli": "^5.0.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@@ -79,12 +80,12 @@
 | 
				
			|||||||
    "dev": "encore dev",
 | 
					    "dev": "encore dev",
 | 
				
			||||||
    "watch": "encore dev --watch",
 | 
					    "watch": "encore dev --watch",
 | 
				
			||||||
    "build": "encore production --progress",
 | 
					    "build": "encore production --progress",
 | 
				
			||||||
    "specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml> templates/api/specs.yaml",
 | 
					    "specs-build": "yaml-merge src/Bundle/ChillMainBundle/chill.api.specs.yaml src/Bundle/ChillPersonBundle/chill.api.specs.yaml src/Bundle/ChillCalendarBundle/chill.api.specs.yaml src/Bundle/ChillThirdPartyBundle/chill.api.specs.yaml src/Bundle/ChillDocStoreBundle/chill.api.specs.yaml src/Bundle/ChillTicketBundle/chill.api.specs.yaml> templates/api/specs.yaml",
 | 
				
			||||||
    "specs-validate": "swagger-cli validate templates/api/specs.yaml",
 | 
					    "specs-validate": "swagger-cli validate templates/api/specs.yaml",
 | 
				
			||||||
    "specs-create-dir": "mkdir -p templates/api",
 | 
					    "specs-create-dir": "mkdir -p templates/api",
 | 
				
			||||||
    "specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate",
 | 
					    "specs": "yarn run specs-create-dir && yarn run specs-build && yarn run specs-validate",
 | 
				
			||||||
    "version": "node --version",
 | 
					    "version": "node --version",
 | 
				
			||||||
    "eslint": "npx eslint-baseline --fix \"src/**/*.{js,ts,vue}\""
 | 
					    "eslint": "eslint-baseline --fix \"src/**/*.{js,ts,vue}\""
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "private": true
 | 
					  "private": true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,10 @@
 | 
				
			|||||||
      <!-- temporarily removed, the time to find a fix -->
 | 
					      <!-- temporarily removed, the time to find a fix -->
 | 
				
			||||||
      <exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
 | 
					      <exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
 | 
				
			||||||
    </testsuite>
 | 
					    </testsuite>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <testsuite name="TicketBundle">
 | 
				
			||||||
 | 
					      <directory suffix="Test.php">src/Bundle/ChillTicketBundle/tests/</directory>
 | 
				
			||||||
 | 
					    </testsuite>
 | 
				
			||||||
    <!--
 | 
					    <!--
 | 
				
			||||||
        <testsuite name="ReportBundle">
 | 
					        <testsuite name="ReportBundle">
 | 
				
			||||||
            <directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>
 | 
					            <directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										8
									
								
								resources/ticket_motives_import/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								resources/ticket_motives_import/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					In this directory, you find an example of file for the command `chill:main:ticket_motives_import`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This file contains a list of ticket motives to import into the system. Each entry is a dictionary with two keys: `code` and `label`. The `code` key contains the unique code for the ticket motive, and the `label` key contains the human-readable label for the ticket motive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `stored_objects` key contains the documents that will be associated with the tickets. They must be found in the same directory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The command `chill:main:ticket_motives_import` uses this file to import the specified ticket motives into the system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										136
									
								
								resources/ticket_motives_import/motives.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								resources/ticket_motives_import/motives.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: Appel famille pour annonce de décès
 | 
				
			||||||
 | 
					  urgent: false
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Date du décès
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: lieu du décès (domicile ou hôpital)
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: nom de l’hôpital
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: service concerné
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 2_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 3_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 4_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: 'Appel famille pour annonce absence : hospitalisation ou consultation'
 | 
				
			||||||
 | 
					  urgent: false
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Quel hôpital
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: quel service
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: pour quelles raisons
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 'consultation : date et heure'
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: hospitalisation complète ou HDJ
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 5_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 6_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 7_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: 'Appel famille pour annonce absence : interruption de prise en charge'
 | 
				
			||||||
 | 
					  urgent: false
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Pour quelles raisons ? Date
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: durée
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: accord médical ?
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 8_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 9_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 10_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: 'Appel famille pour annonce absence : changement d’adresse'
 | 
				
			||||||
 | 
					  urgent: false
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Où
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Pourquoi ? Pour combien de temps ? Besoin d’un relais des soins ? Nouvelle adresse ?
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 11_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 12_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 13_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: Appel famille pour altération de l’état général du patient
 | 
				
			||||||
 | 
					  urgent: true
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Recherche des symptômes
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Attentes par rapport à la demande
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 14_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 15_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 16_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: Appel famille pour prise en charge de la douleur
 | 
				
			||||||
 | 
					  urgent: true
 | 
				
			||||||
 | 
					  supplementary_informations:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Localisation douleur
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Horaire dernier passage
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: Traitements en cours
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 17_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 18_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 19_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					- label:
 | 
				
			||||||
 | 
					    fr: Appel famille pour information sur la date de prise en charge
 | 
				
			||||||
 | 
					  urgent: false
 | 
				
			||||||
 | 
					  supplementary_informations: []
 | 
				
			||||||
 | 
					  stored_objects:
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: ☀️ De 07h à 21h
 | 
				
			||||||
 | 
					    filename: 20_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🌙 De 21h à 07h du matin
 | 
				
			||||||
 | 
					    filename: 21_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
 | 
					  - label:
 | 
				
			||||||
 | 
					      fr: 🗓️ Dimanches et jours fériés
 | 
				
			||||||
 | 
					    filename: 22_doc_20250402_Pelotons flux externes consolidés.pdf
 | 
				
			||||||
							
								
								
									
										6
									
								
								resources/translation_override/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								resources/translation_override/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					In this directory, you find an example of file for the command `chill:main:override_translation`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This file contains a list of translations to override in the translation catalogue. Each entry is a dictionary with two keys: `from` and `to`. The `from` key contains the original translation string, and the `to` key contains the replacement string.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The command `chill:main:override_translation` uses this file to generate a new translation catalogue with the specified overrides applied.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								resources/translation_override/overrides.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								resources/translation_override/overrides.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					- {from: "de l'usager", to: "du patient"}
 | 
				
			||||||
 | 
					- {from: "l'usager", to: "le patient"}
 | 
				
			||||||
 | 
					- {from: "L'usager", to: "Le patient"}
 | 
				
			||||||
 | 
					- {from: "d'usagers", to: "de patients"}
 | 
				
			||||||
 | 
					- {from: "usagers", to: "patients"}
 | 
				
			||||||
 | 
					- {from: "Usagers", to: "Patients"}
 | 
				
			||||||
 | 
					- {from: "usager", to: "patient"}
 | 
				
			||||||
 | 
					- {from: "Usager", to: "Patient"}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <concerned-groups v-if="hasPerson" />
 | 
					  <concerned-groups v-if="hasPerson" />
 | 
				
			||||||
    <social-issues-acc v-if="hasSocialIssues" />
 | 
					  <social-issues-acc v-if="hasSocialIssues" />
 | 
				
			||||||
    <location v-if="hasLocation" />
 | 
					  <location v-if="hasLocation" />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -10,12 +10,12 @@ import SocialIssuesAcc from "./components/SocialIssuesAcc.vue";
 | 
				
			|||||||
import Location from "./components/Location.vue";
 | 
					import Location from "./components/Location.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "App",
 | 
					  name: "App",
 | 
				
			||||||
    props: ["hasSocialIssues", "hasLocation", "hasPerson"],
 | 
					  props: ["hasSocialIssues", "hasLocation", "hasPerson"],
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        ConcernedGroups,
 | 
					    ConcernedGroups,
 | 
				
			||||||
        SocialIssuesAcc,
 | 
					    SocialIssuesAcc,
 | 
				
			||||||
        Location,
 | 
					    Location,
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,46 +1,43 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <teleport to="#add-persons" v-if="isComponentVisible">
 | 
					  <teleport to="#add-persons" v-if="isComponentVisible">
 | 
				
			||||||
        <div class="flex-bloc concerned-groups" :class="getContext">
 | 
					    <div class="flex-bloc concerned-groups" :class="getContext">
 | 
				
			||||||
            <persons-bloc
 | 
					      <persons-bloc
 | 
				
			||||||
                v-for="bloc in contextPersonsBlocs"
 | 
					        v-for="bloc in contextPersonsBlocs"
 | 
				
			||||||
                :key="bloc.key"
 | 
					        :key="bloc.key"
 | 
				
			||||||
                :bloc="bloc"
 | 
					        :bloc="bloc"
 | 
				
			||||||
                :bloc-width="getBlocWidth"
 | 
					        :bloc-width="getBlocWidth"
 | 
				
			||||||
                :set-persons-in-bloc="setPersonsInBloc"
 | 
					        :set-persons-in-bloc="setPersonsInBloc"
 | 
				
			||||||
            />
 | 
					      />
 | 
				
			||||||
        </div>
 | 
					    </div>
 | 
				
			||||||
        <div
 | 
					    <div
 | 
				
			||||||
            v-if="
 | 
					      v-if="getContext === 'accompanyingCourse' && suggestedEntities.length > 0"
 | 
				
			||||||
                getContext === 'accompanyingCourse' &&
 | 
					    >
 | 
				
			||||||
                suggestedEntities.length > 0
 | 
					      <ul class="list-suggest add-items inline">
 | 
				
			||||||
            "
 | 
					        <li
 | 
				
			||||||
 | 
					          v-for="(p, i) in suggestedEntities"
 | 
				
			||||||
 | 
					          @click="addSuggestedEntity(p)"
 | 
				
			||||||
 | 
					          :key="`suggestedEntities-${i}`"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <ul class="list-suggest add-items inline">
 | 
					          <person-text v-if="p.type === 'person'" :person="p" />
 | 
				
			||||||
                <li
 | 
					          <span v-else>{{ p.text }}</span>
 | 
				
			||||||
                    v-for="(p, i) in suggestedEntities"
 | 
					        </li>
 | 
				
			||||||
                    @click="addSuggestedEntity(p)"
 | 
					      </ul>
 | 
				
			||||||
                    :key="`suggestedEntities-${i}`"
 | 
					    </div>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <person-text v-if="p.type === 'person'" :person="p" />
 | 
					 | 
				
			||||||
                    <span v-else>{{ p.text }}</span>
 | 
					 | 
				
			||||||
                </li>
 | 
					 | 
				
			||||||
            </ul>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ul class="record_actions">
 | 
					    <ul class="record_actions">
 | 
				
			||||||
            <li class="add-persons">
 | 
					      <li class="add-persons">
 | 
				
			||||||
                <add-persons
 | 
					        <add-persons
 | 
				
			||||||
                    :buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
 | 
					          :buttonTitle="trans(ACTIVITY_ADD_PERSONS)"
 | 
				
			||||||
                    :modalTitle="trans(ACTIVITY_ADD_PERSONS)"
 | 
					          :modalTitle="trans(ACTIVITY_ADD_PERSONS)"
 | 
				
			||||||
                    v-bind:key="addPersons.key"
 | 
					          v-bind:key="addPersons.key"
 | 
				
			||||||
                    v-bind:options="addPersonsOptions"
 | 
					          v-bind:options="addPersonsOptions"
 | 
				
			||||||
                    @addNewPersons="addNewPersons"
 | 
					          @addNewPersons="addNewPersons"
 | 
				
			||||||
                    ref="addPersons"
 | 
					          ref="addPersons"
 | 
				
			||||||
                >
 | 
					        >
 | 
				
			||||||
                </add-persons>
 | 
					        </add-persons>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
        </ul>
 | 
					    </ul>
 | 
				
			||||||
    </teleport>
 | 
					  </teleport>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -49,208 +46,208 @@ import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
 | 
				
			|||||||
import PersonsBloc from "./ConcernedGroups/PersonsBloc.vue";
 | 
					import PersonsBloc from "./ConcernedGroups/PersonsBloc.vue";
 | 
				
			||||||
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
 | 
					import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ACTIVITY_BLOC_PERSONS,
 | 
					  ACTIVITY_BLOC_PERSONS,
 | 
				
			||||||
    ACTIVITY_BLOC_PERSONS_ASSOCIATED,
 | 
					  ACTIVITY_BLOC_PERSONS_ASSOCIATED,
 | 
				
			||||||
    ACTIVITY_BLOC_THIRDPARTY,
 | 
					  ACTIVITY_BLOC_THIRDPARTY,
 | 
				
			||||||
    ACTIVITY_BLOC_USERS,
 | 
					  ACTIVITY_BLOC_USERS,
 | 
				
			||||||
    ACTIVITY_ADD_PERSONS,
 | 
					  ACTIVITY_ADD_PERSONS,
 | 
				
			||||||
    trans,
 | 
					  trans,
 | 
				
			||||||
} from "translator";
 | 
					} from "translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "ConcernedGroups",
 | 
					  name: "ConcernedGroups",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        AddPersons,
 | 
					    AddPersons,
 | 
				
			||||||
        PersonsBloc,
 | 
					    PersonsBloc,
 | 
				
			||||||
        PersonText,
 | 
					    PersonText,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      trans,
 | 
				
			||||||
 | 
					      ACTIVITY_ADD_PERSONS,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      personsBlocs: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          key: "persons",
 | 
				
			||||||
 | 
					          title: trans(ACTIVITY_BLOC_PERSONS),
 | 
				
			||||||
 | 
					          persons: [],
 | 
				
			||||||
 | 
					          included: false,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          key: "personsAssociated",
 | 
				
			||||||
 | 
					          title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
 | 
				
			||||||
 | 
					          persons: [],
 | 
				
			||||||
 | 
					          included: window.activity
 | 
				
			||||||
 | 
					            ? window.activity.activityType.personsVisible !== 0
 | 
				
			||||||
 | 
					            : true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          key: "personsNotAssociated",
 | 
				
			||||||
 | 
					          title: "activity.bloc_persons_not_associated",
 | 
				
			||||||
 | 
					          persons: [],
 | 
				
			||||||
 | 
					          included: window.activity
 | 
				
			||||||
 | 
					            ? window.activity.activityType.personsVisible !== 0
 | 
				
			||||||
 | 
					            : true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          key: "thirdparty",
 | 
				
			||||||
 | 
					          title: trans(ACTIVITY_BLOC_THIRDPARTY),
 | 
				
			||||||
 | 
					          persons: [],
 | 
				
			||||||
 | 
					          included: window.activity
 | 
				
			||||||
 | 
					            ? window.activity.activityType.thirdPartiesVisible !== 0
 | 
				
			||||||
 | 
					            : true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          key: "users",
 | 
				
			||||||
 | 
					          title: trans(ACTIVITY_BLOC_USERS),
 | 
				
			||||||
 | 
					          persons: [],
 | 
				
			||||||
 | 
					          included: window.activity
 | 
				
			||||||
 | 
					            ? window.activity.activityType.usersVisible !== 0
 | 
				
			||||||
 | 
					            : true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      addPersons: {
 | 
				
			||||||
 | 
					        key: "activity",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    isComponentVisible() {
 | 
				
			||||||
 | 
					      return window.activity
 | 
				
			||||||
 | 
					        ? window.activity.activityType.personsVisible !== 0 ||
 | 
				
			||||||
 | 
					            window.activity.activityType.thirdPartiesVisible !== 0 ||
 | 
				
			||||||
 | 
					            window.activity.activityType.usersVisible !== 0
 | 
				
			||||||
 | 
					        : true;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    ...mapState({
 | 
				
			||||||
        return {
 | 
					      persons: (state) => state.activity.persons,
 | 
				
			||||||
            trans,
 | 
					      thirdParties: (state) => state.activity.thirdParties,
 | 
				
			||||||
            ACTIVITY_ADD_PERSONS,
 | 
					      users: (state) => state.activity.users,
 | 
				
			||||||
        };
 | 
					      accompanyingCourse: (state) => state.activity.accompanyingPeriod,
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    ...mapGetters(["suggestedEntities"]),
 | 
				
			||||||
 | 
					    getContext() {
 | 
				
			||||||
 | 
					      return this.accompanyingCourse ? "accompanyingCourse" : "person";
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					    contextPersonsBlocs() {
 | 
				
			||||||
        return {
 | 
					      return this.personsBlocs.filter((bloc) => bloc.included !== false);
 | 
				
			||||||
            personsBlocs: [
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    key: "persons",
 | 
					 | 
				
			||||||
                    title: trans(ACTIVITY_BLOC_PERSONS),
 | 
					 | 
				
			||||||
                    persons: [],
 | 
					 | 
				
			||||||
                    included: false,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    key: "personsAssociated",
 | 
					 | 
				
			||||||
                    title: trans(ACTIVITY_BLOC_PERSONS_ASSOCIATED),
 | 
					 | 
				
			||||||
                    persons: [],
 | 
					 | 
				
			||||||
                    included: window.activity
 | 
					 | 
				
			||||||
                        ? window.activity.activityType.personsVisible !== 0
 | 
					 | 
				
			||||||
                        : true,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    key: "personsNotAssociated",
 | 
					 | 
				
			||||||
                    title: "activity.bloc_persons_not_associated",
 | 
					 | 
				
			||||||
                    persons: [],
 | 
					 | 
				
			||||||
                    included: window.activity
 | 
					 | 
				
			||||||
                        ? window.activity.activityType.personsVisible !== 0
 | 
					 | 
				
			||||||
                        : true,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    key: "thirdparty",
 | 
					 | 
				
			||||||
                    title: trans(ACTIVITY_BLOC_THIRDPARTY),
 | 
					 | 
				
			||||||
                    persons: [],
 | 
					 | 
				
			||||||
                    included: window.activity
 | 
					 | 
				
			||||||
                        ? window.activity.activityType.thirdPartiesVisible !== 0
 | 
					 | 
				
			||||||
                        : true,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    key: "users",
 | 
					 | 
				
			||||||
                    title: trans(ACTIVITY_BLOC_USERS),
 | 
					 | 
				
			||||||
                    persons: [],
 | 
					 | 
				
			||||||
                    included: window.activity
 | 
					 | 
				
			||||||
                        ? window.activity.activityType.usersVisible !== 0
 | 
					 | 
				
			||||||
                        : true,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            addPersons: {
 | 
					 | 
				
			||||||
                key: "activity",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    addPersonsOptions() {
 | 
				
			||||||
        isComponentVisible() {
 | 
					      let optionsType = [];
 | 
				
			||||||
            return window.activity
 | 
					      if (window.activity) {
 | 
				
			||||||
                ? window.activity.activityType.personsVisible !== 0 ||
 | 
					        if (window.activity.activityType.personsVisible !== 0) {
 | 
				
			||||||
                      window.activity.activityType.thirdPartiesVisible !== 0 ||
 | 
					          optionsType.push("person");
 | 
				
			||||||
                      window.activity.activityType.usersVisible !== 0
 | 
					        }
 | 
				
			||||||
                : true;
 | 
					        if (window.activity.activityType.thirdPartiesVisible !== 0) {
 | 
				
			||||||
        },
 | 
					          optionsType.push("thirdparty");
 | 
				
			||||||
        ...mapState({
 | 
					        }
 | 
				
			||||||
            persons: (state) => state.activity.persons,
 | 
					        if (window.activity.activityType.usersVisible !== 0) {
 | 
				
			||||||
            thirdParties: (state) => state.activity.thirdParties,
 | 
					          optionsType.push("user");
 | 
				
			||||||
            users: (state) => state.activity.users,
 | 
					        }
 | 
				
			||||||
            accompanyingCourse: (state) => state.activity.accompanyingPeriod,
 | 
					      } else {
 | 
				
			||||||
        }),
 | 
					        optionsType = ["person", "thirdparty", "user"];
 | 
				
			||||||
        ...mapGetters(["suggestedEntities"]),
 | 
					      }
 | 
				
			||||||
        getContext() {
 | 
					      return {
 | 
				
			||||||
            return this.accompanyingCourse ? "accompanyingCourse" : "person";
 | 
					        type: optionsType,
 | 
				
			||||||
        },
 | 
					        priority: null,
 | 
				
			||||||
        contextPersonsBlocs() {
 | 
					        uniq: false,
 | 
				
			||||||
            return this.personsBlocs.filter((bloc) => bloc.included !== false);
 | 
					        button: {
 | 
				
			||||||
        },
 | 
					          size: "btn-sm",
 | 
				
			||||||
        addPersonsOptions() {
 | 
					 | 
				
			||||||
            let optionsType = [];
 | 
					 | 
				
			||||||
            if (window.activity) {
 | 
					 | 
				
			||||||
                if (window.activity.activityType.personsVisible !== 0) {
 | 
					 | 
				
			||||||
                    optionsType.push("person");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (window.activity.activityType.thirdPartiesVisible !== 0) {
 | 
					 | 
				
			||||||
                    optionsType.push("thirdparty");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (window.activity.activityType.usersVisible !== 0) {
 | 
					 | 
				
			||||||
                    optionsType.push("user");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                optionsType = ["person", "thirdparty", "user"];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                type: optionsType,
 | 
					 | 
				
			||||||
                priority: null,
 | 
					 | 
				
			||||||
                uniq: false,
 | 
					 | 
				
			||||||
                button: {
 | 
					 | 
				
			||||||
                    size: "btn-sm",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        getBlocWidth() {
 | 
					 | 
				
			||||||
            return Math.round(100 / this.contextPersonsBlocs.length) + "%";
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mounted() {
 | 
					    getBlocWidth() {
 | 
				
			||||||
        this.setPersonsInBloc();
 | 
					      return Math.round(100 / this.contextPersonsBlocs.length) + "%";
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					  },
 | 
				
			||||||
        setPersonsInBloc() {
 | 
					  mounted() {
 | 
				
			||||||
            let groups;
 | 
					    this.setPersonsInBloc();
 | 
				
			||||||
            if (this.accompanyingCourse) {
 | 
					  },
 | 
				
			||||||
                groups = this.splitPersonsInGroups();
 | 
					  methods: {
 | 
				
			||||||
            }
 | 
					    setPersonsInBloc() {
 | 
				
			||||||
            this.personsBlocs.forEach((bloc) => {
 | 
					      let groups;
 | 
				
			||||||
                if (this.accompanyingCourse) {
 | 
					      if (this.accompanyingCourse) {
 | 
				
			||||||
                    switch (bloc.key) {
 | 
					        groups = this.splitPersonsInGroups();
 | 
				
			||||||
                        case "personsAssociated":
 | 
					      }
 | 
				
			||||||
                            bloc.persons = groups.personsAssociated;
 | 
					      this.personsBlocs.forEach((bloc) => {
 | 
				
			||||||
                            bloc.included = true;
 | 
					        if (this.accompanyingCourse) {
 | 
				
			||||||
                            break;
 | 
					          switch (bloc.key) {
 | 
				
			||||||
                        case "personsNotAssociated":
 | 
					            case "personsAssociated":
 | 
				
			||||||
                            bloc.persons = groups.personsNotAssociated;
 | 
					              bloc.persons = groups.personsAssociated;
 | 
				
			||||||
                            bloc.included = true;
 | 
					              bloc.included = true;
 | 
				
			||||||
                            break;
 | 
					              break;
 | 
				
			||||||
                    }
 | 
					            case "personsNotAssociated":
 | 
				
			||||||
                } else {
 | 
					              bloc.persons = groups.personsNotAssociated;
 | 
				
			||||||
                    switch (bloc.key) {
 | 
					              bloc.included = true;
 | 
				
			||||||
                        case "persons":
 | 
					              break;
 | 
				
			||||||
                            bloc.persons = this.persons;
 | 
					          }
 | 
				
			||||||
                            bloc.included = true;
 | 
					        } else {
 | 
				
			||||||
                            break;
 | 
					          switch (bloc.key) {
 | 
				
			||||||
                    }
 | 
					            case "persons":
 | 
				
			||||||
                }
 | 
					              bloc.persons = this.persons;
 | 
				
			||||||
                switch (bloc.key) {
 | 
					              bloc.included = true;
 | 
				
			||||||
                    case "thirdparty":
 | 
					              break;
 | 
				
			||||||
                        bloc.persons = this.thirdParties;
 | 
					          }
 | 
				
			||||||
                        break;
 | 
					        }
 | 
				
			||||||
                    case "users":
 | 
					        switch (bloc.key) {
 | 
				
			||||||
                        bloc.persons = this.users;
 | 
					          case "thirdparty":
 | 
				
			||||||
                        break;
 | 
					            bloc.persons = this.thirdParties;
 | 
				
			||||||
                }
 | 
					            break;
 | 
				
			||||||
            }, groups);
 | 
					          case "users":
 | 
				
			||||||
        },
 | 
					            bloc.persons = this.users;
 | 
				
			||||||
        splitPersonsInGroups() {
 | 
					            break;
 | 
				
			||||||
            let personsAssociated = [];
 | 
					        }
 | 
				
			||||||
            let personsNotAssociated = this.persons;
 | 
					      }, groups);
 | 
				
			||||||
            let participations = this.getCourseParticipations();
 | 
					 | 
				
			||||||
            this.persons.forEach((person) => {
 | 
					 | 
				
			||||||
                participations.forEach((participation) => {
 | 
					 | 
				
			||||||
                    if (person.id === participation.id) {
 | 
					 | 
				
			||||||
                        //console.log(person.id);
 | 
					 | 
				
			||||||
                        personsAssociated.push(person);
 | 
					 | 
				
			||||||
                        personsNotAssociated = personsNotAssociated.filter(
 | 
					 | 
				
			||||||
                            (p) => p !== person,
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                personsAssociated: personsAssociated,
 | 
					 | 
				
			||||||
                personsNotAssociated: personsNotAssociated,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        getCourseParticipations() {
 | 
					 | 
				
			||||||
            let participations = [];
 | 
					 | 
				
			||||||
            this.accompanyingCourse.participations.forEach((participation) => {
 | 
					 | 
				
			||||||
                if (!participation.endDate) {
 | 
					 | 
				
			||||||
                    participations.push(participation.person);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            return participations;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addNewPersons({ selected, modal }) {
 | 
					 | 
				
			||||||
            console.log("@@@ CLICK button addNewPersons", selected);
 | 
					 | 
				
			||||||
            selected.forEach((item) => {
 | 
					 | 
				
			||||||
                this.$store.dispatch("addPersonsInvolved", item);
 | 
					 | 
				
			||||||
            }, this);
 | 
					 | 
				
			||||||
            this.$refs.addPersons.resetSearch(); // to cast child method
 | 
					 | 
				
			||||||
            modal.showModal = false;
 | 
					 | 
				
			||||||
            this.setPersonsInBloc();
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addSuggestedEntity(person) {
 | 
					 | 
				
			||||||
            this.$store.dispatch("addPersonsInvolved", {
 | 
					 | 
				
			||||||
                result: person,
 | 
					 | 
				
			||||||
                type: "person",
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            this.setPersonsInBloc();
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    splitPersonsInGroups() {
 | 
				
			||||||
 | 
					      let personsAssociated = [];
 | 
				
			||||||
 | 
					      let personsNotAssociated = this.persons;
 | 
				
			||||||
 | 
					      let participations = this.getCourseParticipations();
 | 
				
			||||||
 | 
					      this.persons.forEach((person) => {
 | 
				
			||||||
 | 
					        participations.forEach((participation) => {
 | 
				
			||||||
 | 
					          if (person.id === participation.id) {
 | 
				
			||||||
 | 
					            //console.log(person.id);
 | 
				
			||||||
 | 
					            personsAssociated.push(person);
 | 
				
			||||||
 | 
					            personsNotAssociated = personsNotAssociated.filter(
 | 
				
			||||||
 | 
					              (p) => p !== person,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        personsAssociated: personsAssociated,
 | 
				
			||||||
 | 
					        personsNotAssociated: personsNotAssociated,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getCourseParticipations() {
 | 
				
			||||||
 | 
					      let participations = [];
 | 
				
			||||||
 | 
					      this.accompanyingCourse.participations.forEach((participation) => {
 | 
				
			||||||
 | 
					        if (!participation.endDate) {
 | 
				
			||||||
 | 
					          participations.push(participation.person);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return participations;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    addNewPersons({ selected, modal }) {
 | 
				
			||||||
 | 
					      console.log("@@@ CLICK button addNewPersons", selected);
 | 
				
			||||||
 | 
					      selected.forEach((item) => {
 | 
				
			||||||
 | 
					        this.$store.dispatch("addPersonsInvolved", item);
 | 
				
			||||||
 | 
					      }, this);
 | 
				
			||||||
 | 
					      this.$refs.addPersons.resetSearch(); // to cast child method
 | 
				
			||||||
 | 
					      modal.showModal = false;
 | 
				
			||||||
 | 
					      this.setPersonsInBloc();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    addSuggestedEntity(person) {
 | 
				
			||||||
 | 
					      this.$store.dispatch("addPersonsInvolved", {
 | 
				
			||||||
 | 
					        result: person,
 | 
				
			||||||
 | 
					        type: "person",
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      this.setPersonsInBloc();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,29 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <li>
 | 
					  <li>
 | 
				
			||||||
        <span :title="person.text" @click.prevent="$emit('remove', person)">
 | 
					    <span :title="person.text" @click.prevent="$emit('remove', person)">
 | 
				
			||||||
            <span class="chill_denomination">
 | 
					      <span class="chill_denomination">
 | 
				
			||||||
                <person-text :person="person" :is-cut="true" />
 | 
					        <person-text :person="person" :is-cut="true" />
 | 
				
			||||||
            </span>
 | 
					      </span>
 | 
				
			||||||
        </span>
 | 
					    </span>
 | 
				
			||||||
    </li>
 | 
					  </li>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
 | 
					import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "PersonBadge",
 | 
					  name: "PersonBadge",
 | 
				
			||||||
    props: ["person"],
 | 
					  props: ["person"],
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        PersonText,
 | 
					    PersonText,
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
    // computed: {
 | 
					  // computed: {
 | 
				
			||||||
    //    textCutted() {
 | 
					  //    textCutted() {
 | 
				
			||||||
    //       let more = (this.person.text.length > 15) ?'…' : '';
 | 
					  //       let more = (this.person.text.length > 15) ?'…' : '';
 | 
				
			||||||
    //       return this.person.text.slice(0,15) + more;
 | 
					  //       return this.person.text.slice(0,15) + more;
 | 
				
			||||||
    //    }
 | 
					  //    }
 | 
				
			||||||
    // },
 | 
					  // },
 | 
				
			||||||
    emits: ["remove"],
 | 
					  emits: ["remove"],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,38 +1,38 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
 | 
					  <div class="item-bloc" :style="{ 'flex-basis': blocWidth }">
 | 
				
			||||||
        <div class="item-row">
 | 
					    <div class="item-row">
 | 
				
			||||||
            <div class="item-col">
 | 
					      <div class="item-col">
 | 
				
			||||||
                <h4>{{ $t(bloc.title) }}</h4>
 | 
					        <h4>{{ $t(bloc.title) }}</h4>
 | 
				
			||||||
            </div>
 | 
					      </div>
 | 
				
			||||||
            <div class="item-col">
 | 
					      <div class="item-col">
 | 
				
			||||||
                <ul class="list-suggest remove-items">
 | 
					        <ul class="list-suggest remove-items">
 | 
				
			||||||
                    <person-badge
 | 
					          <person-badge
 | 
				
			||||||
                        v-for="person in bloc.persons"
 | 
					            v-for="person in bloc.persons"
 | 
				
			||||||
                        :key="person.id"
 | 
					            :key="person.id"
 | 
				
			||||||
                        :person="person"
 | 
					            :person="person"
 | 
				
			||||||
                        @remove="removePerson"
 | 
					            @remove="removePerson"
 | 
				
			||||||
                    />
 | 
					          />
 | 
				
			||||||
                </ul>
 | 
					        </ul>
 | 
				
			||||||
            </div>
 | 
					      </div>
 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import PersonBadge from "./PersonBadge.vue";
 | 
					import PersonBadge from "./PersonBadge.vue";
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "PersonsBloc",
 | 
					  name: "PersonsBloc",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        PersonBadge,
 | 
					    PersonBadge,
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
    props: ["bloc", "setPersonsInBloc", "blocWidth"],
 | 
					  props: ["bloc", "setPersonsInBloc", "blocWidth"],
 | 
				
			||||||
    methods: {
 | 
					  methods: {
 | 
				
			||||||
        removePerson(item) {
 | 
					    removePerson(item) {
 | 
				
			||||||
            console.log("@@ CLICK remove person: item", item);
 | 
					      console.log("@@ CLICK remove person: item", item);
 | 
				
			||||||
            this.$store.dispatch("removePersonInvolved", item);
 | 
					      this.$store.dispatch("removePersonInvolved", item);
 | 
				
			||||||
            this.setPersonsInBloc();
 | 
					      this.setPersonsInBloc();
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,32 +1,32 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <teleport to="#location">
 | 
					  <teleport to="#location">
 | 
				
			||||||
        <div class="mb-3 row">
 | 
					    <div class="mb-3 row">
 | 
				
			||||||
            <label :class="locationClassList">
 | 
					      <label :class="locationClassList">
 | 
				
			||||||
                {{ trans(ACTIVITY_LOCATION) }}
 | 
					        {{ trans(ACTIVITY_LOCATION) }}
 | 
				
			||||||
            </label>
 | 
					      </label>
 | 
				
			||||||
            <div class="col-sm-8">
 | 
					      <div class="col-sm-8">
 | 
				
			||||||
                <VueMultiselect
 | 
					        <VueMultiselect
 | 
				
			||||||
                    name="selectLocation"
 | 
					          name="selectLocation"
 | 
				
			||||||
                    id="selectLocation"
 | 
					          id="selectLocation"
 | 
				
			||||||
                    label="name"
 | 
					          label="name"
 | 
				
			||||||
                    track-by="id"
 | 
					          track-by="id"
 | 
				
			||||||
                    open-direction="top"
 | 
					          open-direction="top"
 | 
				
			||||||
                    :multiple="false"
 | 
					          :multiple="false"
 | 
				
			||||||
                    :searchable="true"
 | 
					          :searchable="true"
 | 
				
			||||||
                    :placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
 | 
					          :placeholder="trans(ACTIVITY_CHOOSE_LOCATION)"
 | 
				
			||||||
                    :custom-label="customLabel"
 | 
					          :custom-label="customLabel"
 | 
				
			||||||
                    :select-label="trans(MULTISELECT_SELECT_LABEL)"
 | 
					          :select-label="trans(MULTISELECT_SELECT_LABEL)"
 | 
				
			||||||
                    :deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
 | 
					          :deselect-label="trans(MULTISELECT_DESELECT_LABEL)"
 | 
				
			||||||
                    :selected-label="trans(MULTISELECT_SELECTED_LABEL)"
 | 
					          :selected-label="trans(MULTISELECT_SELECTED_LABEL)"
 | 
				
			||||||
                    :options="availableLocations"
 | 
					          :options="availableLocations"
 | 
				
			||||||
                    group-values="locations"
 | 
					          group-values="locations"
 | 
				
			||||||
                    group-label="locationGroup"
 | 
					          group-label="locationGroup"
 | 
				
			||||||
                    v-model="location"
 | 
					          v-model="location"
 | 
				
			||||||
                />
 | 
					        />
 | 
				
			||||||
                <new-location v-bind:available-locations="availableLocations" />
 | 
					        <new-location v-bind:available-locations="availableLocations" />
 | 
				
			||||||
            </div>
 | 
					      </div>
 | 
				
			||||||
        </div>
 | 
					    </div>
 | 
				
			||||||
    </teleport>
 | 
					  </teleport>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -35,60 +35,60 @@ import VueMultiselect from "vue-multiselect";
 | 
				
			|||||||
import NewLocation from "./Location/NewLocation.vue";
 | 
					import NewLocation from "./Location/NewLocation.vue";
 | 
				
			||||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
					import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    trans,
 | 
					  trans,
 | 
				
			||||||
    ACTIVITY_LOCATION,
 | 
					  ACTIVITY_LOCATION,
 | 
				
			||||||
    ACTIVITY_CHOOSE_LOCATION,
 | 
					  ACTIVITY_CHOOSE_LOCATION,
 | 
				
			||||||
    MULTISELECT_SELECT_LABEL,
 | 
					  MULTISELECT_SELECT_LABEL,
 | 
				
			||||||
    MULTISELECT_DESELECT_LABEL,
 | 
					  MULTISELECT_DESELECT_LABEL,
 | 
				
			||||||
    MULTISELECT_SELECTED_LABEL,
 | 
					  MULTISELECT_SELECTED_LABEL,
 | 
				
			||||||
} from "translator";
 | 
					} from "translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "Location",
 | 
					  name: "Location",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        NewLocation,
 | 
					    NewLocation,
 | 
				
			||||||
        VueMultiselect,
 | 
					    VueMultiselect,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      trans,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION,
 | 
				
			||||||
 | 
					      ACTIVITY_CHOOSE_LOCATION,
 | 
				
			||||||
 | 
					      MULTISELECT_SELECT_LABEL,
 | 
				
			||||||
 | 
					      MULTISELECT_DESELECT_LABEL,
 | 
				
			||||||
 | 
					      MULTISELECT_SELECTED_LABEL,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    ...mapState(["activity", "availableLocations"]),
 | 
				
			||||||
 | 
					    ...mapGetters(["suggestedEntities"]),
 | 
				
			||||||
 | 
					    location: {
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.activity.location;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.$store.dispatch("updateLocation", value);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					  },
 | 
				
			||||||
        return {
 | 
					  methods: {
 | 
				
			||||||
            trans,
 | 
					    labelAccompanyingCourseLocation(value) {
 | 
				
			||||||
            ACTIVITY_LOCATION,
 | 
					      return `${value.address.text} (${localizeString(value.locationType.title)})`;
 | 
				
			||||||
            ACTIVITY_CHOOSE_LOCATION,
 | 
					 | 
				
			||||||
            MULTISELECT_SELECT_LABEL,
 | 
					 | 
				
			||||||
            MULTISELECT_DESELECT_LABEL,
 | 
					 | 
				
			||||||
            MULTISELECT_SELECTED_LABEL,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					    customLabel(value) {
 | 
				
			||||||
        return {
 | 
					      return value.locationType
 | 
				
			||||||
            locationClassList: `col-form-label col-sm-4 ${document.querySelector("input#chill_activitybundle_activity_location").getAttribute("required") ? "required" : ""}`,
 | 
					        ? value.name
 | 
				
			||||||
        };
 | 
					          ? value.name === "__AccompanyingCourseLocation__"
 | 
				
			||||||
    },
 | 
					            ? this.labelAccompanyingCourseLocation(value)
 | 
				
			||||||
    computed: {
 | 
					            : `${value.name} (${localizeString(value.locationType.title)})`
 | 
				
			||||||
        ...mapState(["activity", "availableLocations"]),
 | 
					          : localizeString(value.locationType.title)
 | 
				
			||||||
        ...mapGetters(["suggestedEntities"]),
 | 
					        : "";
 | 
				
			||||||
        location: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.activity.location;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.$store.dispatch("updateLocation", value);
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    methods: {
 | 
					 | 
				
			||||||
        labelAccompanyingCourseLocation(value) {
 | 
					 | 
				
			||||||
            return `${value.address.text} (${localizeString(value.locationType.title)})`;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        customLabel(value) {
 | 
					 | 
				
			||||||
            return value.locationType
 | 
					 | 
				
			||||||
                ? value.name
 | 
					 | 
				
			||||||
                    ? value.name === "__AccompanyingCourseLocation__"
 | 
					 | 
				
			||||||
                        ? this.labelAccompanyingCourseLocation(value)
 | 
					 | 
				
			||||||
                        : `${value.name} (${localizeString(value.locationType.title)})`
 | 
					 | 
				
			||||||
                    : localizeString(value.locationType.title)
 | 
					 | 
				
			||||||
                : "";
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,123 +1,114 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div>
 | 
					  <div>
 | 
				
			||||||
        <ul class="record_actions">
 | 
					    <ul class="record_actions">
 | 
				
			||||||
            <li>
 | 
					      <li>
 | 
				
			||||||
                <a class="btn btn-sm btn-create" @click="openModal">
 | 
					        <a class="btn btn-sm btn-create" @click="openModal">
 | 
				
			||||||
                    {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
 | 
					          {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
 | 
				
			||||||
                </a>
 | 
					        </a>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
        </ul>
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <teleport to="body">
 | 
					    <teleport to="body">
 | 
				
			||||||
            <modal
 | 
					      <modal
 | 
				
			||||||
                v-if="modal.showModal"
 | 
					        v-if="modal.showModal"
 | 
				
			||||||
                :modalDialogClass="modal.modalDialogClass"
 | 
					        :modalDialogClass="modal.modalDialogClass"
 | 
				
			||||||
                @close="modal.showModal = false"
 | 
					        @close="modal.showModal = false"
 | 
				
			||||||
            >
 | 
					      >
 | 
				
			||||||
                <template #header>
 | 
					        <template #header>
 | 
				
			||||||
                    <h3 class="modal-title">
 | 
					          <h3 class="modal-title">
 | 
				
			||||||
                        {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
 | 
					            {{ trans(ACTIVITY_CREATE_NEW_LOCATION) }}
 | 
				
			||||||
                    </h3>
 | 
					          </h3>
 | 
				
			||||||
                </template>
 | 
					        </template>
 | 
				
			||||||
                <template #body>
 | 
					        <template #body>
 | 
				
			||||||
                    <form>
 | 
					          <form>
 | 
				
			||||||
                        <div class="alert alert-warning" v-if="errors.length">
 | 
					            <div class="alert alert-warning" v-if="errors.length">
 | 
				
			||||||
                            <ul>
 | 
					              <ul>
 | 
				
			||||||
                                <li v-for="(e, i) in errors" :key="i">
 | 
					                <li v-for="(e, i) in errors" :key="i">
 | 
				
			||||||
                                    {{ e }}
 | 
					                  {{ e }}
 | 
				
			||||||
                                </li>
 | 
					                </li>
 | 
				
			||||||
                            </ul>
 | 
					              </ul>
 | 
				
			||||||
                        </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <div class="form-floating mb-3">
 | 
					            <div class="form-floating mb-3">
 | 
				
			||||||
                            <select
 | 
					              <select
 | 
				
			||||||
                                class="form-select form-select-lg"
 | 
					                class="form-select form-select-lg"
 | 
				
			||||||
                                id="type"
 | 
					                id="type"
 | 
				
			||||||
                                required
 | 
					                required
 | 
				
			||||||
                                v-model="selectType"
 | 
					                v-model="selectType"
 | 
				
			||||||
                            >
 | 
					              >
 | 
				
			||||||
                                <option selected disabled value="">
 | 
					                <option selected disabled value="">
 | 
				
			||||||
                                    {{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
 | 
					                  {{ trans(ACTIVITY_CHOOSE_LOCATION_TYPE) }}
 | 
				
			||||||
                                </option>
 | 
					                </option>
 | 
				
			||||||
                                <option
 | 
					                <option v-for="t in locationTypes" :value="t" :key="t.id">
 | 
				
			||||||
                                    v-for="t in locationTypes"
 | 
					                  {{ localizeString(t.title) }}
 | 
				
			||||||
                                    :value="t"
 | 
					                </option>
 | 
				
			||||||
                                    :key="t.id"
 | 
					              </select>
 | 
				
			||||||
                                >
 | 
					              <label>{{ trans(ACTIVITY_LOCATION_FIELDS_TYPE) }}</label>
 | 
				
			||||||
                                    {{ localizeString(t.title) }}
 | 
					            </div>
 | 
				
			||||||
                                </option>
 | 
					 | 
				
			||||||
                            </select>
 | 
					 | 
				
			||||||
                            <label>{{
 | 
					 | 
				
			||||||
                                trans(ACTIVITY_LOCATION_FIELDS_TYPE)
 | 
					 | 
				
			||||||
                            }}</label>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <div class="form-floating mb-3">
 | 
					            <div class="form-floating mb-3">
 | 
				
			||||||
                            <input
 | 
					              <input
 | 
				
			||||||
                                class="form-control form-control-lg"
 | 
					                class="form-control form-control-lg"
 | 
				
			||||||
                                id="name"
 | 
					                id="name"
 | 
				
			||||||
                                v-model="inputName"
 | 
					                v-model="inputName"
 | 
				
			||||||
                                placeholder
 | 
					                placeholder
 | 
				
			||||||
                            />
 | 
					              />
 | 
				
			||||||
                            <label for="name">{{
 | 
					              <label for="name">{{
 | 
				
			||||||
                                trans(ACTIVITY_LOCATION_FIELDS_NAME)
 | 
					                trans(ACTIVITY_LOCATION_FIELDS_NAME)
 | 
				
			||||||
                            }}</label>
 | 
					              }}</label>
 | 
				
			||||||
                        </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <add-address
 | 
					            <add-address
 | 
				
			||||||
                            :context="addAddress.context"
 | 
					              :context="addAddress.context"
 | 
				
			||||||
                            :options="addAddress.options"
 | 
					              :options="addAddress.options"
 | 
				
			||||||
                            :addressChangedCallback="submitNewAddress"
 | 
					              :addressChangedCallback="submitNewAddress"
 | 
				
			||||||
                            v-if="showAddAddress"
 | 
					              v-if="showAddAddress"
 | 
				
			||||||
                            ref="addAddress"
 | 
					              ref="addAddress"
 | 
				
			||||||
                        />
 | 
					            />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <div class="form-floating mb-3" v-if="showContactData">
 | 
					            <div class="form-floating mb-3" v-if="showContactData">
 | 
				
			||||||
                            <input
 | 
					              <input
 | 
				
			||||||
                                class="form-control form-control-lg"
 | 
					                class="form-control form-control-lg"
 | 
				
			||||||
                                id="phonenumber1"
 | 
					                id="phonenumber1"
 | 
				
			||||||
                                v-model="inputPhonenumber1"
 | 
					                v-model="inputPhonenumber1"
 | 
				
			||||||
                                placeholder
 | 
					                placeholder
 | 
				
			||||||
                            />
 | 
					              />
 | 
				
			||||||
                            <label for="phonenumber1">{{
 | 
					              <label for="phonenumber1">{{
 | 
				
			||||||
                                trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
 | 
					                trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER1)
 | 
				
			||||||
                            }}</label>
 | 
					              }}</label>
 | 
				
			||||||
                        </div>
 | 
					            </div>
 | 
				
			||||||
                        <div class="form-floating mb-3" v-if="hasPhonenumber1">
 | 
					            <div class="form-floating mb-3" v-if="hasPhonenumber1">
 | 
				
			||||||
                            <input
 | 
					              <input
 | 
				
			||||||
                                class="form-control form-control-lg"
 | 
					                class="form-control form-control-lg"
 | 
				
			||||||
                                id="phonenumber2"
 | 
					                id="phonenumber2"
 | 
				
			||||||
                                v-model="inputPhonenumber2"
 | 
					                v-model="inputPhonenumber2"
 | 
				
			||||||
                                placeholder
 | 
					                placeholder
 | 
				
			||||||
                            />
 | 
					              />
 | 
				
			||||||
                            <label for="phonenumber2">{{
 | 
					              <label for="phonenumber2">{{
 | 
				
			||||||
                                trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
 | 
					                trans(ACTIVITY_LOCATION_FIELDS_PHONENUMBER2)
 | 
				
			||||||
                            }}</label>
 | 
					              }}</label>
 | 
				
			||||||
                        </div>
 | 
					            </div>
 | 
				
			||||||
                        <div class="form-floating mb-3" v-if="showContactData">
 | 
					            <div class="form-floating mb-3" v-if="showContactData">
 | 
				
			||||||
                            <input
 | 
					              <input
 | 
				
			||||||
                                class="form-control form-control-lg"
 | 
					                class="form-control form-control-lg"
 | 
				
			||||||
                                id="email"
 | 
					                id="email"
 | 
				
			||||||
                                v-model="inputEmail"
 | 
					                v-model="inputEmail"
 | 
				
			||||||
                                placeholder
 | 
					                placeholder
 | 
				
			||||||
                            />
 | 
					              />
 | 
				
			||||||
                            <label for="email">{{
 | 
					              <label for="email">{{
 | 
				
			||||||
                                trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
 | 
					                trans(ACTIVITY_LOCATION_FIELDS_EMAIL)
 | 
				
			||||||
                            }}</label>
 | 
					              }}</label>
 | 
				
			||||||
                        </div>
 | 
					            </div>
 | 
				
			||||||
                    </form>
 | 
					          </form>
 | 
				
			||||||
                </template>
 | 
					        </template>
 | 
				
			||||||
                <template #footer>
 | 
					        <template #footer>
 | 
				
			||||||
                    <button
 | 
					          <button class="btn btn-save" @click.prevent="saveNewLocation">
 | 
				
			||||||
                        class="btn btn-save"
 | 
					            {{ trans(SAVE) }}
 | 
				
			||||||
                        @click.prevent="saveNewLocation"
 | 
					          </button>
 | 
				
			||||||
                    >
 | 
					        </template>
 | 
				
			||||||
                        {{ trans(SAVE) }}
 | 
					      </modal>
 | 
				
			||||||
                    </button>
 | 
					    </teleport>
 | 
				
			||||||
                </template>
 | 
					  </div>
 | 
				
			||||||
            </modal>
 | 
					 | 
				
			||||||
        </teleport>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -128,237 +119,236 @@ import { getLocationTypes } from "../../api";
 | 
				
			|||||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
 | 
					import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
 | 
				
			||||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
					import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    SAVE,
 | 
					  SAVE,
 | 
				
			||||||
    ACTIVITY_LOCATION_FIELDS_EMAIL,
 | 
					  ACTIVITY_LOCATION_FIELDS_EMAIL,
 | 
				
			||||||
    ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
 | 
					  ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
 | 
				
			||||||
    ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
 | 
					  ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
 | 
				
			||||||
    ACTIVITY_LOCATION_FIELDS_NAME,
 | 
					  ACTIVITY_LOCATION_FIELDS_NAME,
 | 
				
			||||||
    ACTIVITY_LOCATION_FIELDS_TYPE,
 | 
					  ACTIVITY_LOCATION_FIELDS_TYPE,
 | 
				
			||||||
    ACTIVITY_CHOOSE_LOCATION_TYPE,
 | 
					  ACTIVITY_CHOOSE_LOCATION_TYPE,
 | 
				
			||||||
    ACTIVITY_CREATE_NEW_LOCATION,
 | 
					  ACTIVITY_CREATE_NEW_LOCATION,
 | 
				
			||||||
    trans,
 | 
					  trans,
 | 
				
			||||||
} from "translator";
 | 
					} from "translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "NewLocation",
 | 
					  name: "NewLocation",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        Modal,
 | 
					    Modal,
 | 
				
			||||||
        AddAddress,
 | 
					    AddAddress,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      trans,
 | 
				
			||||||
 | 
					      SAVE,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION_FIELDS_EMAIL,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION_FIELDS_NAME,
 | 
				
			||||||
 | 
					      ACTIVITY_LOCATION_FIELDS_TYPE,
 | 
				
			||||||
 | 
					      ACTIVITY_CHOOSE_LOCATION_TYPE,
 | 
				
			||||||
 | 
					      ACTIVITY_CREATE_NEW_LOCATION,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  props: ["availableLocations"],
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      errors: [],
 | 
				
			||||||
 | 
					      selected: {
 | 
				
			||||||
 | 
					        type: null,
 | 
				
			||||||
 | 
					        name: null,
 | 
				
			||||||
 | 
					        addressId: null,
 | 
				
			||||||
 | 
					        phonenumber1: null,
 | 
				
			||||||
 | 
					        phonenumber2: null,
 | 
				
			||||||
 | 
					        email: null,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      locationTypes: [],
 | 
				
			||||||
 | 
					      modal: {
 | 
				
			||||||
 | 
					        showModal: false,
 | 
				
			||||||
 | 
					        modalDialogClass: "modal-dialog-scrollable modal-xl",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      addAddress: {
 | 
				
			||||||
 | 
					        options: {
 | 
				
			||||||
 | 
					          button: {
 | 
				
			||||||
 | 
					            text: {
 | 
				
			||||||
 | 
					              create: "activity.create_address",
 | 
				
			||||||
 | 
					              edit: "activity.edit_address",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            size: "btn-sm",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          title: {
 | 
				
			||||||
 | 
					            create: "activity.create_address",
 | 
				
			||||||
 | 
					            edit: "activity.edit_address",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        context: {
 | 
				
			||||||
 | 
					          target: {
 | 
				
			||||||
 | 
					            //name, id
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          edit: false,
 | 
				
			||||||
 | 
					          addressId: null,
 | 
				
			||||||
 | 
					          defaults: window.addaddress,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    ...mapState(["activity"]),
 | 
				
			||||||
 | 
					    selectType: {
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.selected.type;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.selected.type = value;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    inputName: {
 | 
				
			||||||
        return {
 | 
					      get() {
 | 
				
			||||||
            trans,
 | 
					        return this.selected.name;
 | 
				
			||||||
            SAVE,
 | 
					      },
 | 
				
			||||||
            ACTIVITY_LOCATION_FIELDS_EMAIL,
 | 
					      set(value) {
 | 
				
			||||||
            ACTIVITY_LOCATION_FIELDS_PHONENUMBER1,
 | 
					        this.selected.name = value;
 | 
				
			||||||
            ACTIVITY_LOCATION_FIELDS_PHONENUMBER2,
 | 
					      },
 | 
				
			||||||
            ACTIVITY_LOCATION_FIELDS_NAME,
 | 
					    },
 | 
				
			||||||
            ACTIVITY_LOCATION_FIELDS_TYPE,
 | 
					    inputEmail: {
 | 
				
			||||||
            ACTIVITY_CHOOSE_LOCATION_TYPE,
 | 
					      get() {
 | 
				
			||||||
            ACTIVITY_CREATE_NEW_LOCATION,
 | 
					        return this.selected.email;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.selected.email = value;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    inputPhonenumber1: {
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.selected.phonenumber1;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.selected.phonenumber1 = value;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    inputPhonenumber2: {
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.selected.phonenumber2;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.selected.phonenumber2 = value;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    hasPhonenumber1() {
 | 
				
			||||||
 | 
					      return (
 | 
				
			||||||
 | 
					        this.selected.phonenumber1 !== null && this.selected.phonenumber1 !== ""
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    showAddAddress() {
 | 
				
			||||||
 | 
					      let cond = false;
 | 
				
			||||||
 | 
					      if (this.selected.type) {
 | 
				
			||||||
 | 
					        if (this.selected.type.addressRequired !== "never") {
 | 
				
			||||||
 | 
					          cond = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return cond;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    showContactData() {
 | 
				
			||||||
 | 
					      let cond = false;
 | 
				
			||||||
 | 
					      if (this.selected.type) {
 | 
				
			||||||
 | 
					        if (this.selected.type.contactData !== "never") {
 | 
				
			||||||
 | 
					          cond = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return cond;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    this.getLocationTypesList();
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    localizeString,
 | 
				
			||||||
 | 
					    checkForm() {
 | 
				
			||||||
 | 
					      let cond = true;
 | 
				
			||||||
 | 
					      this.errors = [];
 | 
				
			||||||
 | 
					      if (!this.selected.type) {
 | 
				
			||||||
 | 
					        this.errors.push("Type de localisation requis");
 | 
				
			||||||
 | 
					        cond = false;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.selected.type.addressRequired === "required" &&
 | 
				
			||||||
 | 
					          !this.selected.addressId
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.errors.push("Adresse requise");
 | 
				
			||||||
 | 
					          cond = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.selected.type.contactData === "required" &&
 | 
				
			||||||
 | 
					          !this.selected.phonenumber1
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.errors.push("Numéro de téléphone requis");
 | 
				
			||||||
 | 
					          cond = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.selected.type.contactData === "required" &&
 | 
				
			||||||
 | 
					          !this.selected.email
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.errors.push("Adresse email requise");
 | 
				
			||||||
 | 
					          cond = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return cond;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getLocationTypesList() {
 | 
				
			||||||
 | 
					      getLocationTypes().then((results) => {
 | 
				
			||||||
 | 
					        this.locationTypes = results.filter(
 | 
				
			||||||
 | 
					          (t) => t.availableForUsers === true,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    openModal() {
 | 
				
			||||||
 | 
					      this.modal.showModal = true;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    saveNewLocation() {
 | 
				
			||||||
 | 
					      if (this.checkForm()) {
 | 
				
			||||||
 | 
					        let body = {
 | 
				
			||||||
 | 
					          type: "location",
 | 
				
			||||||
 | 
					          name: this.selected.name,
 | 
				
			||||||
 | 
					          locationType: {
 | 
				
			||||||
 | 
					            id: this.selected.type.id,
 | 
				
			||||||
 | 
					            type: "location-type",
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          phonenumber1: this.selected.phonenumber1,
 | 
				
			||||||
 | 
					          phonenumber2: this.selected.phonenumber2,
 | 
				
			||||||
 | 
					          email: this.selected.email,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    },
 | 
					        if (this.selected.addressId) {
 | 
				
			||||||
    props: ["availableLocations"],
 | 
					          body = Object.assign(body, {
 | 
				
			||||||
    data() {
 | 
					            address: {
 | 
				
			||||||
        return {
 | 
					              id: this.selected.addressId,
 | 
				
			||||||
            errors: [],
 | 
					 | 
				
			||||||
            selected: {
 | 
					 | 
				
			||||||
                type: null,
 | 
					 | 
				
			||||||
                name: null,
 | 
					 | 
				
			||||||
                addressId: null,
 | 
					 | 
				
			||||||
                phonenumber1: null,
 | 
					 | 
				
			||||||
                phonenumber2: null,
 | 
					 | 
				
			||||||
                email: null,
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            locationTypes: [],
 | 
					          });
 | 
				
			||||||
            modal: {
 | 
					        }
 | 
				
			||||||
                showModal: false,
 | 
					 | 
				
			||||||
                modalDialogClass: "modal-dialog-scrollable modal-xl",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            addAddress: {
 | 
					 | 
				
			||||||
                options: {
 | 
					 | 
				
			||||||
                    button: {
 | 
					 | 
				
			||||||
                        text: {
 | 
					 | 
				
			||||||
                            create: "activity.create_address",
 | 
					 | 
				
			||||||
                            edit: "activity.edit_address",
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        size: "btn-sm",
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    title: {
 | 
					 | 
				
			||||||
                        create: "activity.create_address",
 | 
					 | 
				
			||||||
                        edit: "activity.edit_address",
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                context: {
 | 
					 | 
				
			||||||
                    target: {
 | 
					 | 
				
			||||||
                        //name, id
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    edit: false,
 | 
					 | 
				
			||||||
                    addressId: null,
 | 
					 | 
				
			||||||
                    defaults: window.addaddress,
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    computed: {
 | 
					 | 
				
			||||||
        ...mapState(["activity"]),
 | 
					 | 
				
			||||||
        selectType: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.selected.type;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.selected.type = value;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        inputName: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.selected.name;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.selected.name = value;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        inputEmail: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.selected.email;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.selected.email = value;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        inputPhonenumber1: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.selected.phonenumber1;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.selected.phonenumber1 = value;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        inputPhonenumber2: {
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.selected.phonenumber2;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.selected.phonenumber2 = value;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        hasPhonenumber1() {
 | 
					 | 
				
			||||||
            return (
 | 
					 | 
				
			||||||
                this.selected.phonenumber1 !== null &&
 | 
					 | 
				
			||||||
                this.selected.phonenumber1 !== ""
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        showAddAddress() {
 | 
					 | 
				
			||||||
            let cond = false;
 | 
					 | 
				
			||||||
            if (this.selected.type) {
 | 
					 | 
				
			||||||
                if (this.selected.type.addressRequired !== "never") {
 | 
					 | 
				
			||||||
                    cond = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return cond;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        showContactData() {
 | 
					 | 
				
			||||||
            let cond = false;
 | 
					 | 
				
			||||||
            if (this.selected.type) {
 | 
					 | 
				
			||||||
                if (this.selected.type.contactData !== "never") {
 | 
					 | 
				
			||||||
                    cond = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return cond;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    mounted() {
 | 
					 | 
				
			||||||
        this.getLocationTypesList();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    methods: {
 | 
					 | 
				
			||||||
        localizeString,
 | 
					 | 
				
			||||||
        checkForm() {
 | 
					 | 
				
			||||||
            let cond = true;
 | 
					 | 
				
			||||||
            this.errors = [];
 | 
					 | 
				
			||||||
            if (!this.selected.type) {
 | 
					 | 
				
			||||||
                this.errors.push("Type de localisation requis");
 | 
					 | 
				
			||||||
                cond = false;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    this.selected.type.addressRequired === "required" &&
 | 
					 | 
				
			||||||
                    !this.selected.addressId
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    this.errors.push("Adresse requise");
 | 
					 | 
				
			||||||
                    cond = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    this.selected.type.contactData === "required" &&
 | 
					 | 
				
			||||||
                    !this.selected.phonenumber1
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    this.errors.push("Numéro de téléphone requis");
 | 
					 | 
				
			||||||
                    cond = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    this.selected.type.contactData === "required" &&
 | 
					 | 
				
			||||||
                    !this.selected.email
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    this.errors.push("Adresse email requise");
 | 
					 | 
				
			||||||
                    cond = false;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return cond;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        getLocationTypesList() {
 | 
					 | 
				
			||||||
            getLocationTypes().then((results) => {
 | 
					 | 
				
			||||||
                this.locationTypes = results.filter(
 | 
					 | 
				
			||||||
                    (t) => t.availableForUsers === true,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        openModal() {
 | 
					 | 
				
			||||||
            this.modal.showModal = true;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        saveNewLocation() {
 | 
					 | 
				
			||||||
            if (this.checkForm()) {
 | 
					 | 
				
			||||||
                let body = {
 | 
					 | 
				
			||||||
                    type: "location",
 | 
					 | 
				
			||||||
                    name: this.selected.name,
 | 
					 | 
				
			||||||
                    locationType: {
 | 
					 | 
				
			||||||
                        id: this.selected.type.id,
 | 
					 | 
				
			||||||
                        type: "location-type",
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    phonenumber1: this.selected.phonenumber1,
 | 
					 | 
				
			||||||
                    phonenumber2: this.selected.phonenumber2,
 | 
					 | 
				
			||||||
                    email: this.selected.email,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                if (this.selected.addressId) {
 | 
					 | 
				
			||||||
                    body = Object.assign(body, {
 | 
					 | 
				
			||||||
                        address: {
 | 
					 | 
				
			||||||
                            id: this.selected.addressId,
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                makeFetch("POST", "/api/1.0/main/location.json", body)
 | 
					        makeFetch("POST", "/api/1.0/main/location.json", body)
 | 
				
			||||||
                    .then((response) => {
 | 
					          .then((response) => {
 | 
				
			||||||
                        this.$store.dispatch("addAvailableLocationGroup", {
 | 
					            this.$store.dispatch("addAvailableLocationGroup", {
 | 
				
			||||||
                            locationGroup: "Localisations nouvellement créées",
 | 
					              locationGroup: "Localisations nouvellement créées",
 | 
				
			||||||
                            locations: [response],
 | 
					              locations: [response],
 | 
				
			||||||
                        });
 | 
					            });
 | 
				
			||||||
                        this.$store.dispatch("updateLocation", response);
 | 
					            this.$store.dispatch("updateLocation", response);
 | 
				
			||||||
                        this.modal.showModal = false;
 | 
					            this.modal.showModal = false;
 | 
				
			||||||
                    })
 | 
					          })
 | 
				
			||||||
                    .catch((error) => {
 | 
					          .catch((error) => {
 | 
				
			||||||
                        if (error.name === "ValidationException") {
 | 
					            if (error.name === "ValidationException") {
 | 
				
			||||||
                            for (let v of error.violations) {
 | 
					              for (let v of error.violations) {
 | 
				
			||||||
                                this.errors.push(v);
 | 
					                this.errors.push(v);
 | 
				
			||||||
                            }
 | 
					              }
 | 
				
			||||||
                        } else {
 | 
					            } else {
 | 
				
			||||||
                            this.errors.push("An error occurred");
 | 
					              this.errors.push("An error occurred");
 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					          });
 | 
				
			||||||
        submitNewAddress(payload) {
 | 
					      }
 | 
				
			||||||
            this.selected.addressId = payload.addressId;
 | 
					 | 
				
			||||||
            this.addAddress.context.addressId = payload.addressId;
 | 
					 | 
				
			||||||
            this.addAddress.context.edit = true;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    submitNewAddress(payload) {
 | 
				
			||||||
 | 
					      this.selected.addressId = payload.addressId;
 | 
				
			||||||
 | 
					      this.addAddress.context.addressId = payload.addressId;
 | 
				
			||||||
 | 
					      this.addAddress.context.edit = true;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,103 +1,98 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <teleport to="#social-issues-acc">
 | 
					  <teleport to="#social-issues-acc">
 | 
				
			||||||
        <div class="mb-3 row">
 | 
					    <div class="mb-3 row">
 | 
				
			||||||
            <div class="col-4">
 | 
					      <div class="col-4">
 | 
				
			||||||
                <label :class="socialIssuesClassList">{{
 | 
					        <label :class="socialIssuesClassList">{{
 | 
				
			||||||
                    trans(ACTIVITY_SOCIAL_ISSUES)
 | 
					          trans(ACTIVITY_SOCIAL_ISSUES)
 | 
				
			||||||
                }}</label>
 | 
					        }}</label>
 | 
				
			||||||
            </div>
 | 
					      </div>
 | 
				
			||||||
            <div class="col-8">
 | 
					      <div class="col-8">
 | 
				
			||||||
                <check-social-issue
 | 
					        <check-social-issue
 | 
				
			||||||
                    v-for="issue in socialIssuesList"
 | 
					          v-for="issue in socialIssuesList"
 | 
				
			||||||
                    :key="issue.id"
 | 
					          :key="issue.id"
 | 
				
			||||||
                    :issue="issue"
 | 
					          :issue="issue"
 | 
				
			||||||
                    :selection="socialIssuesSelected"
 | 
					          :selection="socialIssuesSelected"
 | 
				
			||||||
                    @updateSelected="updateIssuesSelected"
 | 
					          @updateSelected="updateIssuesSelected"
 | 
				
			||||||
                >
 | 
					        >
 | 
				
			||||||
                </check-social-issue>
 | 
					        </check-social-issue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <div class="my-3">
 | 
					        <div class="my-3">
 | 
				
			||||||
                    <VueMultiselect
 | 
					          <VueMultiselect
 | 
				
			||||||
                        name="otherIssues"
 | 
					            name="otherIssues"
 | 
				
			||||||
                        label="text"
 | 
					            label="text"
 | 
				
			||||||
                        track-by="id"
 | 
					            track-by="id"
 | 
				
			||||||
                        open-direction="bottom"
 | 
					            open-direction="bottom"
 | 
				
			||||||
                        :close-on-select="true"
 | 
					            :close-on-select="true"
 | 
				
			||||||
                        :preserve-search="false"
 | 
					            :preserve-search="false"
 | 
				
			||||||
                        :reset-after="true"
 | 
					            :reset-after="true"
 | 
				
			||||||
                        :hide-selected="true"
 | 
					            :hide-selected="true"
 | 
				
			||||||
                        :taggable="false"
 | 
					            :taggable="false"
 | 
				
			||||||
                        :multiple="false"
 | 
					            :multiple="false"
 | 
				
			||||||
                        :searchable="true"
 | 
					            :searchable="true"
 | 
				
			||||||
                        :allow-empty="true"
 | 
					            :allow-empty="true"
 | 
				
			||||||
                        :show-labels="false"
 | 
					            :show-labels="false"
 | 
				
			||||||
                        :loading="issueIsLoading"
 | 
					            :loading="issueIsLoading"
 | 
				
			||||||
                        :placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
 | 
					            :placeholder="trans(ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE)"
 | 
				
			||||||
                        :options="socialIssuesOther"
 | 
					            :options="socialIssuesOther"
 | 
				
			||||||
                        @select="addIssueInList"
 | 
					            @select="addIssueInList"
 | 
				
			||||||
                    >
 | 
					          >
 | 
				
			||||||
                    </VueMultiselect>
 | 
					          </VueMultiselect>
 | 
				
			||||||
                </div>
 | 
					        </div>
 | 
				
			||||||
            </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="mb-3 row">
 | 
				
			||||||
 | 
					      <div class="col-4">
 | 
				
			||||||
 | 
					        <label :class="socialActionsClassList">{{
 | 
				
			||||||
 | 
					          trans(ACTIVITY_SOCIAL_ACTIONS)
 | 
				
			||||||
 | 
					        }}</label>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="col-8">
 | 
				
			||||||
 | 
					        <div v-if="actionIsLoading === true">
 | 
				
			||||||
 | 
					          <i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="mb-3 row">
 | 
					        <span
 | 
				
			||||||
            <div class="col-4">
 | 
					          v-else-if="socialIssuesSelected.length === 0"
 | 
				
			||||||
                <label :class="socialActionsClassList">{{
 | 
					          class="inline-choice chill-no-data-statement mt-3"
 | 
				
			||||||
                    trans(ACTIVITY_SOCIAL_ACTIONS)
 | 
					        >
 | 
				
			||||||
                }}</label>
 | 
					          {{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
 | 
				
			||||||
            </div>
 | 
					        </span>
 | 
				
			||||||
            <div class="col-8">
 | 
					 | 
				
			||||||
                <div v-if="actionIsLoading === true">
 | 
					 | 
				
			||||||
                    <i
 | 
					 | 
				
			||||||
                        class="chill-green fa fa-circle-o-notch fa-spin fa-lg"
 | 
					 | 
				
			||||||
                    ></i>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <span
 | 
					        <template
 | 
				
			||||||
                    v-else-if="socialIssuesSelected.length === 0"
 | 
					          v-else-if="
 | 
				
			||||||
                    class="inline-choice chill-no-data-statement mt-3"
 | 
					            socialActionsList.length > 0 &&
 | 
				
			||||||
                >
 | 
					            (socialIssuesSelected.length || socialActionsSelected.length)
 | 
				
			||||||
                    {{ trans(ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE) }}
 | 
					          "
 | 
				
			||||||
                </span>
 | 
					        >
 | 
				
			||||||
 | 
					          <div
 | 
				
			||||||
 | 
					            id="actionsList"
 | 
				
			||||||
 | 
					            v-for="group in socialActionsList"
 | 
				
			||||||
 | 
					            :key="group.issue"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <span class="badge bg-chill-l-gray text-dark">{{
 | 
				
			||||||
 | 
					              group.issue
 | 
				
			||||||
 | 
					            }}</span>
 | 
				
			||||||
 | 
					            <check-social-action
 | 
				
			||||||
 | 
					              v-for="action in group.actions"
 | 
				
			||||||
 | 
					              :key="action.id"
 | 
				
			||||||
 | 
					              :action="action"
 | 
				
			||||||
 | 
					              :selection="socialActionsSelected"
 | 
				
			||||||
 | 
					              @updateSelected="updateActionsSelected"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					            </check-social-action>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <template
 | 
					        <span
 | 
				
			||||||
                    v-else-if="
 | 
					          v-else-if="actionAreLoaded && socialActionsList.length === 0"
 | 
				
			||||||
                        socialActionsList.length > 0 &&
 | 
					          class="inline-choice chill-no-data-statement mt-3"
 | 
				
			||||||
                        (socialIssuesSelected.length ||
 | 
					        >
 | 
				
			||||||
                            socialActionsSelected.length)
 | 
					          {{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
 | 
				
			||||||
                    "
 | 
					        </span>
 | 
				
			||||||
                >
 | 
					      </div>
 | 
				
			||||||
                    <div
 | 
					    </div>
 | 
				
			||||||
                        id="actionsList"
 | 
					  </teleport>
 | 
				
			||||||
                        v-for="group in socialActionsList"
 | 
					 | 
				
			||||||
                        :key="group.issue"
 | 
					 | 
				
			||||||
                    >
 | 
					 | 
				
			||||||
                        <span class="badge bg-chill-l-gray text-dark">{{
 | 
					 | 
				
			||||||
                            group.issue
 | 
					 | 
				
			||||||
                        }}</span>
 | 
					 | 
				
			||||||
                        <check-social-action
 | 
					 | 
				
			||||||
                            v-for="action in group.actions"
 | 
					 | 
				
			||||||
                            :key="action.id"
 | 
					 | 
				
			||||||
                            :action="action"
 | 
					 | 
				
			||||||
                            :selection="socialActionsSelected"
 | 
					 | 
				
			||||||
                            @updateSelected="updateActionsSelected"
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                        </check-social-action>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <span
 | 
					 | 
				
			||||||
                    v-else-if="
 | 
					 | 
				
			||||||
                        actionAreLoaded && socialActionsList.length === 0
 | 
					 | 
				
			||||||
                    "
 | 
					 | 
				
			||||||
                    class="inline-choice chill-no-data-statement mt-3"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    {{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </teleport>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -106,154 +101,153 @@ import CheckSocialIssue from "./SocialIssuesAcc/CheckSocialIssue.vue";
 | 
				
			|||||||
import CheckSocialAction from "./SocialIssuesAcc/CheckSocialAction.vue";
 | 
					import CheckSocialAction from "./SocialIssuesAcc/CheckSocialAction.vue";
 | 
				
			||||||
import { getSocialIssues, getSocialActionByIssue } from "../api.js";
 | 
					import { getSocialIssues, getSocialActionByIssue } from "../api.js";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
 | 
					  ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
 | 
				
			||||||
    ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
 | 
					  ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
 | 
				
			||||||
    ACTIVITY_SOCIAL_ACTIONS,
 | 
					  ACTIVITY_SOCIAL_ACTIONS,
 | 
				
			||||||
    ACTIVITY_SOCIAL_ISSUES,
 | 
					  ACTIVITY_SOCIAL_ISSUES,
 | 
				
			||||||
    ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
 | 
					  ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
 | 
				
			||||||
    trans,
 | 
					  trans,
 | 
				
			||||||
} from "translator";
 | 
					} from "translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "SocialIssuesAcc",
 | 
					  name: "SocialIssuesAcc",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        CheckSocialIssue,
 | 
					    CheckSocialIssue,
 | 
				
			||||||
        CheckSocialAction,
 | 
					    CheckSocialAction,
 | 
				
			||||||
        VueMultiselect,
 | 
					    VueMultiselect,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  setup() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      trans,
 | 
				
			||||||
 | 
					      ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
 | 
				
			||||||
 | 
					      ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
 | 
				
			||||||
 | 
					      ACTIVITY_SOCIAL_ACTIONS,
 | 
				
			||||||
 | 
					      ACTIVITY_SOCIAL_ISSUES,
 | 
				
			||||||
 | 
					      ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      issueIsLoading: false,
 | 
				
			||||||
 | 
					      actionIsLoading: false,
 | 
				
			||||||
 | 
					      actionAreLoaded: false,
 | 
				
			||||||
 | 
					      socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`,
 | 
				
			||||||
 | 
					      socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    socialIssuesList() {
 | 
				
			||||||
 | 
					      return this.$store.state.activity.accompanyingPeriod.socialIssues;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setup() {
 | 
					    socialIssuesSelected() {
 | 
				
			||||||
        return {
 | 
					      return this.$store.state.activity.socialIssues;
 | 
				
			||||||
            trans,
 | 
					 | 
				
			||||||
            ACTIVITY_SOCIAL_ACTION_LIST_EMPTY,
 | 
					 | 
				
			||||||
            ACTIVITY_SELECT_FIRST_A_SOCIAL_ISSUE,
 | 
					 | 
				
			||||||
            ACTIVITY_SOCIAL_ACTIONS,
 | 
					 | 
				
			||||||
            ACTIVITY_SOCIAL_ISSUES,
 | 
					 | 
				
			||||||
            ACTIVITY_CHOOSE_OTHER_SOCIAL_ISSUE,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					    socialIssuesOther() {
 | 
				
			||||||
        return {
 | 
					      return this.$store.state.socialIssuesOther;
 | 
				
			||||||
            issueIsLoading: false,
 | 
					 | 
				
			||||||
            actionIsLoading: false,
 | 
					 | 
				
			||||||
            actionAreLoaded: false,
 | 
					 | 
				
			||||||
            socialIssuesClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialIssues").getAttribute("required") ? "required" : ""}`,
 | 
					 | 
				
			||||||
            socialActionsClassList: `col-form-label ${document.querySelector("input#chill_activitybundle_activity_socialActions").getAttribute("required") ? "required" : ""}`,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    socialActionsList() {
 | 
				
			||||||
        socialIssuesList() {
 | 
					      return this.$store.getters.socialActionsListSorted;
 | 
				
			||||||
            return this.$store.state.activity.accompanyingPeriod.socialIssues;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        socialIssuesSelected() {
 | 
					 | 
				
			||||||
            return this.$store.state.activity.socialIssues;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        socialIssuesOther() {
 | 
					 | 
				
			||||||
            return this.$store.state.socialIssuesOther;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        socialActionsList() {
 | 
					 | 
				
			||||||
            return this.$store.getters.socialActionsListSorted;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        socialActionsSelected() {
 | 
					 | 
				
			||||||
            return this.$store.state.activity.socialActions;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mounted() {
 | 
					    socialActionsSelected() {
 | 
				
			||||||
        /* Load other issues in multiselect */
 | 
					      return this.$store.state.activity.socialActions;
 | 
				
			||||||
        this.issueIsLoading = true;
 | 
					 | 
				
			||||||
        this.actionAreLoaded = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        getSocialIssues().then((response) => {
 | 
					 | 
				
			||||||
            /* Add issues to the store */
 | 
					 | 
				
			||||||
            this.$store.commit("updateIssuesOther", response);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Add in list the issues already associated (if not yet listed) */
 | 
					 | 
				
			||||||
            this.socialIssuesSelected.forEach((issue) => {
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    this.socialIssuesList.filter((i) => i.id === issue.id)
 | 
					 | 
				
			||||||
                        .length !== 1
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    this.$store.commit("addIssueInList", issue);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Remove from multiselect the issues that are not yet in the checkbox list */
 | 
					 | 
				
			||||||
            this.socialIssuesList.forEach((issue) => {
 | 
					 | 
				
			||||||
                this.$store.commit("removeIssueInOther", issue);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Filter issues */
 | 
					 | 
				
			||||||
            this.$store.commit("filterList", "issues");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Add in list the actions already associated (if not yet listed) */
 | 
					 | 
				
			||||||
            this.socialActionsSelected.forEach((action) => {
 | 
					 | 
				
			||||||
                this.$store.commit("addActionInList", action);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            /* Filter actions */
 | 
					 | 
				
			||||||
            this.$store.commit("filterList", "actions");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.issueIsLoading = false;
 | 
					 | 
				
			||||||
            this.actionAreLoaded = true;
 | 
					 | 
				
			||||||
            this.updateActionsList();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					  },
 | 
				
			||||||
        /* When choosing an issue in multiselect, add it in checkboxes (as selected),
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    /* Load other issues in multiselect */
 | 
				
			||||||
 | 
					    this.issueIsLoading = true;
 | 
				
			||||||
 | 
					    this.actionAreLoaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getSocialIssues().then((response) => {
 | 
				
			||||||
 | 
					      /* Add issues to the store */
 | 
				
			||||||
 | 
					      this.$store.commit("updateIssuesOther", response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Add in list the issues already associated (if not yet listed) */
 | 
				
			||||||
 | 
					      this.socialIssuesSelected.forEach((issue) => {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          this.socialIssuesList.filter((i) => i.id === issue.id).length !== 1
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          this.$store.commit("addIssueInList", issue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Remove from multiselect the issues that are not yet in the checkbox list */
 | 
				
			||||||
 | 
					      this.socialIssuesList.forEach((issue) => {
 | 
				
			||||||
 | 
					        this.$store.commit("removeIssueInOther", issue);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Filter issues */
 | 
				
			||||||
 | 
					      this.$store.commit("filterList", "issues");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Add in list the actions already associated (if not yet listed) */
 | 
				
			||||||
 | 
					      this.socialActionsSelected.forEach((action) => {
 | 
				
			||||||
 | 
					        this.$store.commit("addActionInList", action);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* Filter actions */
 | 
				
			||||||
 | 
					      this.$store.commit("filterList", "actions");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.issueIsLoading = false;
 | 
				
			||||||
 | 
					      this.actionAreLoaded = true;
 | 
				
			||||||
 | 
					      this.updateActionsList();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    /* When choosing an issue in multiselect, add it in checkboxes (as selected),
 | 
				
			||||||
         remove it from multiselect, and add socialActions concerned
 | 
					         remove it from multiselect, and add socialActions concerned
 | 
				
			||||||
      */
 | 
					      */
 | 
				
			||||||
        addIssueInList(value) {
 | 
					    addIssueInList(value) {
 | 
				
			||||||
            //console.log('addIssueInList', value);
 | 
					      //console.log('addIssueInList', value);
 | 
				
			||||||
            this.$store.commit("addIssueInList", value);
 | 
					      this.$store.commit("addIssueInList", value);
 | 
				
			||||||
            this.$store.commit("removeIssueInOther", value);
 | 
					      this.$store.commit("removeIssueInOther", value);
 | 
				
			||||||
            this.$store.dispatch("addIssueSelected", value);
 | 
					      this.$store.dispatch("addIssueSelected", value);
 | 
				
			||||||
            this.updateActionsList();
 | 
					      this.updateActionsList();
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        /* Update value for selected issues checkboxes
 | 
					    /* Update value for selected issues checkboxes
 | 
				
			||||||
         */
 | 
					     */
 | 
				
			||||||
        updateIssuesSelected(issues) {
 | 
					    updateIssuesSelected(issues) {
 | 
				
			||||||
            //console.log('updateIssuesSelected', issues);
 | 
					      //console.log('updateIssuesSelected', issues);
 | 
				
			||||||
            this.$store.dispatch("updateIssuesSelected", issues);
 | 
					      this.$store.dispatch("updateIssuesSelected", issues);
 | 
				
			||||||
            this.updateActionsList();
 | 
					      this.updateActionsList();
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        /* Update value for selected actions checkboxes
 | 
					    /* Update value for selected actions checkboxes
 | 
				
			||||||
         */
 | 
					     */
 | 
				
			||||||
        updateActionsSelected(actions) {
 | 
					    updateActionsSelected(actions) {
 | 
				
			||||||
            //console.log('updateActionsSelected', actions);
 | 
					      //console.log('updateActionsSelected', actions);
 | 
				
			||||||
            this.$store.dispatch("updateActionsSelected", actions);
 | 
					      this.$store.dispatch("updateActionsSelected", actions);
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        /* Add socialActions concerned: after reset, loop on each issue selected
 | 
					    /* Add socialActions concerned: after reset, loop on each issue selected
 | 
				
			||||||
         to get social actions concerned
 | 
					         to get social actions concerned
 | 
				
			||||||
      */
 | 
					      */
 | 
				
			||||||
        updateActionsList() {
 | 
					    updateActionsList() {
 | 
				
			||||||
            this.resetActionsList();
 | 
					      this.resetActionsList();
 | 
				
			||||||
            this.socialIssuesSelected.forEach((item) => {
 | 
					      this.socialIssuesSelected.forEach((item) => {
 | 
				
			||||||
                this.actionIsLoading = true;
 | 
					        this.actionIsLoading = true;
 | 
				
			||||||
                getSocialActionByIssue(item.id).then(
 | 
					        getSocialActionByIssue(item.id).then(
 | 
				
			||||||
                    (actions) =>
 | 
					          (actions) =>
 | 
				
			||||||
                        new Promise((resolve) => {
 | 
					            new Promise((resolve) => {
 | 
				
			||||||
                            actions.results.forEach((action) => {
 | 
					              actions.results.forEach((action) => {
 | 
				
			||||||
                                this.$store.commit("addActionInList", action);
 | 
					                this.$store.commit("addActionInList", action);
 | 
				
			||||||
                            }, this);
 | 
					              }, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            this.$store.commit("filterList", "actions");
 | 
					              this.$store.commit("filterList", "actions");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            this.actionIsLoading = false;
 | 
					              this.actionIsLoading = false;
 | 
				
			||||||
                            this.actionAreLoaded = true;
 | 
					              this.actionAreLoaded = true;
 | 
				
			||||||
                            resolve();
 | 
					              resolve();
 | 
				
			||||||
                        }),
 | 
					            }),
 | 
				
			||||||
                );
 | 
					        );
 | 
				
			||||||
            }, this);
 | 
					      }, this);
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        /* Reset socialActions List: flush list and restore selected actions
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        resetActionsList() {
 | 
					 | 
				
			||||||
            this.$store.commit("resetActionsList");
 | 
					 | 
				
			||||||
            this.actionAreLoaded = false;
 | 
					 | 
				
			||||||
            this.socialActionsSelected.forEach((item) => {
 | 
					 | 
				
			||||||
                this.$store.commit("addActionInList", item);
 | 
					 | 
				
			||||||
            }, this);
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    /* Reset socialActions List: flush list and restore selected actions
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    resetActionsList() {
 | 
				
			||||||
 | 
					      this.$store.commit("resetActionsList");
 | 
				
			||||||
 | 
					      this.actionAreLoaded = false;
 | 
				
			||||||
 | 
					      this.socialActionsSelected.forEach((item) => {
 | 
				
			||||||
 | 
					        this.$store.commit("addActionInList", item);
 | 
				
			||||||
 | 
					      }, this);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -263,18 +257,18 @@ export default {
 | 
				
			|||||||
@import "ChillMainAssets/chill/scss/chill_variables";
 | 
					@import "ChillMainAssets/chill/scss/chill_variables";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
span.multiselect__single {
 | 
					span.multiselect__single {
 | 
				
			||||||
    display: none !important;
 | 
					  display: none !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#actionsList {
 | 
					#actionsList {
 | 
				
			||||||
    border-radius: 0.5rem;
 | 
					  border-radius: 0.5rem;
 | 
				
			||||||
    padding: 1rem;
 | 
					  padding: 1rem;
 | 
				
			||||||
    margin: 0.5rem;
 | 
					  margin: 0.5rem;
 | 
				
			||||||
    background-color: whitesmoke;
 | 
					  background-color: whitesmoke;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
span.badge {
 | 
					span.badge {
 | 
				
			||||||
    margin-bottom: 0.5rem;
 | 
					  margin-bottom: 0.5rem;
 | 
				
			||||||
    @include badge_social($social-issue-color);
 | 
					  @include badge_social($social-issue-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,38 +1,38 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <span class="inline-choice">
 | 
					  <span class="inline-choice">
 | 
				
			||||||
        <div class="form-check">
 | 
					    <div class="form-check">
 | 
				
			||||||
            <input
 | 
					      <input
 | 
				
			||||||
                class="form-check-input"
 | 
					        class="form-check-input"
 | 
				
			||||||
                type="checkbox"
 | 
					        type="checkbox"
 | 
				
			||||||
                v-model="selected"
 | 
					        v-model="selected"
 | 
				
			||||||
                name="action"
 | 
					        name="action"
 | 
				
			||||||
                :id="action.id"
 | 
					        :id="action.id"
 | 
				
			||||||
                :value="action"
 | 
					        :value="action"
 | 
				
			||||||
            />
 | 
					      />
 | 
				
			||||||
            <label class="form-check-label" :for="action.id">
 | 
					      <label class="form-check-label" :for="action.id">
 | 
				
			||||||
                <span class="badge bg-light text-dark" :title="action.text">{{
 | 
					        <span class="badge bg-light text-dark" :title="action.text">{{
 | 
				
			||||||
                    action.text
 | 
					          action.text
 | 
				
			||||||
                }}</span>
 | 
					        }}</span>
 | 
				
			||||||
            </label>
 | 
					      </label>
 | 
				
			||||||
        </div>
 | 
					    </div>
 | 
				
			||||||
    </span>
 | 
					  </span>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "CheckSocialAction",
 | 
					  name: "CheckSocialAction",
 | 
				
			||||||
    props: ["action", "selection"],
 | 
					  props: ["action", "selection"],
 | 
				
			||||||
    emits: ["updateSelected"],
 | 
					  emits: ["updateSelected"],
 | 
				
			||||||
    computed: {
 | 
					  computed: {
 | 
				
			||||||
        selected: {
 | 
					    selected: {
 | 
				
			||||||
            set(value) {
 | 
					      set(value) {
 | 
				
			||||||
                this.$emit("updateSelected", value);
 | 
					        this.$emit("updateSelected", value);
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
            get() {
 | 
					      get() {
 | 
				
			||||||
                return this.selection;
 | 
					        return this.selection;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,13 +41,13 @@ export default {
 | 
				
			|||||||
@import "ChillPersonAssets/chill/scss/mixins";
 | 
					@import "ChillPersonAssets/chill/scss/mixins";
 | 
				
			||||||
@import "ChillMainAssets/chill/scss/chill_variables";
 | 
					@import "ChillMainAssets/chill/scss/chill_variables";
 | 
				
			||||||
span.badge {
 | 
					span.badge {
 | 
				
			||||||
    @include badge_social($social-action-color);
 | 
					  @include badge_social($social-action-color);
 | 
				
			||||||
    font-size: 95%;
 | 
					  font-size: 95%;
 | 
				
			||||||
    margin-bottom: 5px;
 | 
					  margin-bottom: 5px;
 | 
				
			||||||
    margin-right: 1em;
 | 
					  margin-right: 1em;
 | 
				
			||||||
    max-width: 100%; /* Adjust as needed */
 | 
					  max-width: 100%; /* Adjust as needed */
 | 
				
			||||||
    overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
    text-overflow: ellipsis;
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
    white-space: nowrap;
 | 
					  white-space: nowrap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,38 +1,36 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <span class="inline-choice">
 | 
					  <span class="inline-choice">
 | 
				
			||||||
        <div class="form-check">
 | 
					    <div class="form-check">
 | 
				
			||||||
            <input
 | 
					      <input
 | 
				
			||||||
                class="form-check-input"
 | 
					        class="form-check-input"
 | 
				
			||||||
                type="checkbox"
 | 
					        type="checkbox"
 | 
				
			||||||
                v-model="selected"
 | 
					        v-model="selected"
 | 
				
			||||||
                name="issue"
 | 
					        name="issue"
 | 
				
			||||||
                :id="issue.id"
 | 
					        :id="issue.id"
 | 
				
			||||||
                :value="issue"
 | 
					        :value="issue"
 | 
				
			||||||
            />
 | 
					      />
 | 
				
			||||||
            <label class="form-check-label" :for="issue.id">
 | 
					      <label class="form-check-label" :for="issue.id">
 | 
				
			||||||
                <span class="badge bg-chill-l-gray text-dark">{{
 | 
					        <span class="badge bg-chill-l-gray text-dark">{{ issue.text }}</span>
 | 
				
			||||||
                    issue.text
 | 
					      </label>
 | 
				
			||||||
                }}</span>
 | 
					    </div>
 | 
				
			||||||
            </label>
 | 
					  </span>
 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </span>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "CheckSocialIssue",
 | 
					  name: "CheckSocialIssue",
 | 
				
			||||||
    props: ["issue", "selection"],
 | 
					  props: ["issue", "selection"],
 | 
				
			||||||
    emits: ["updateSelected"],
 | 
					  emits: ["updateSelected"],
 | 
				
			||||||
    computed: {
 | 
					  computed: {
 | 
				
			||||||
        selected: {
 | 
					    selected: {
 | 
				
			||||||
            set(value) {
 | 
					      set(value) {
 | 
				
			||||||
                this.$emit("updateSelected", value);
 | 
					        this.$emit("updateSelected", value);
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
            get() {
 | 
					      get() {
 | 
				
			||||||
                return this.selection;
 | 
					        return this.selection;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,9 +39,9 @@ export default {
 | 
				
			|||||||
@import "ChillPersonAssets/chill/scss/mixins";
 | 
					@import "ChillPersonAssets/chill/scss/mixins";
 | 
				
			||||||
@import "ChillMainAssets/chill/scss/chill_variables";
 | 
					@import "ChillMainAssets/chill/scss/chill_variables";
 | 
				
			||||||
span.badge {
 | 
					span.badge {
 | 
				
			||||||
    @include badge_social($social-issue-color);
 | 
					  @include badge_social($social-issue-color);
 | 
				
			||||||
    font-size: 95%;
 | 
					  font-size: 95%;
 | 
				
			||||||
    margin-bottom: 5px;
 | 
					  margin-bottom: 5px;
 | 
				
			||||||
    margin-right: 1em;
 | 
					  margin-right: 1em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,76 +1,74 @@
 | 
				
			|||||||
import { EventInput } from "@fullcalendar/core";
 | 
					import { EventInput } from "@fullcalendar/core";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    DateTime,
 | 
					  DateTime,
 | 
				
			||||||
    Location,
 | 
					  Location,
 | 
				
			||||||
    User,
 | 
					  User,
 | 
				
			||||||
    UserAssociatedInterface,
 | 
					  UserAssociatedInterface,
 | 
				
			||||||
} from "../../../ChillMainBundle/Resources/public/types";
 | 
					} from "../../../ChillMainBundle/Resources/public/types";
 | 
				
			||||||
import { Person } from "../../../ChillPersonBundle/Resources/public/types";
 | 
					import { Person } from "../../../ChillPersonBundle/Resources/public/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRange {
 | 
					export interface CalendarRange {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    endDate: DateTime;
 | 
					  endDate: DateTime;
 | 
				
			||||||
    startDate: DateTime;
 | 
					  startDate: DateTime;
 | 
				
			||||||
    user: User;
 | 
					  user: User;
 | 
				
			||||||
    location: Location;
 | 
					  location: Location;
 | 
				
			||||||
    createdAt: DateTime;
 | 
					  createdAt: DateTime;
 | 
				
			||||||
    createdBy: User;
 | 
					  createdBy: User;
 | 
				
			||||||
    updatedAt: DateTime;
 | 
					  updatedAt: DateTime;
 | 
				
			||||||
    updatedBy: User;
 | 
					  updatedBy: User;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRangeCreate {
 | 
					export interface CalendarRangeCreate {
 | 
				
			||||||
    user: UserAssociatedInterface;
 | 
					  user: UserAssociatedInterface;
 | 
				
			||||||
    startDate: DateTime;
 | 
					  startDate: DateTime;
 | 
				
			||||||
    endDate: DateTime;
 | 
					  endDate: DateTime;
 | 
				
			||||||
    location: Location;
 | 
					  location: Location;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRangeEdit {
 | 
					export interface CalendarRangeEdit {
 | 
				
			||||||
    startDate?: DateTime;
 | 
					  startDate?: DateTime;
 | 
				
			||||||
    endDate?: DateTime;
 | 
					  endDate?: DateTime;
 | 
				
			||||||
    location?: Location;
 | 
					  location?: Location;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Calendar {
 | 
					export interface Calendar {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarLight {
 | 
					export interface CalendarLight {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    endDate: DateTime;
 | 
					  endDate: DateTime;
 | 
				
			||||||
    startDate: DateTime;
 | 
					  startDate: DateTime;
 | 
				
			||||||
    mainUser: User;
 | 
					  mainUser: User;
 | 
				
			||||||
    persons: Person[];
 | 
					  persons: Person[];
 | 
				
			||||||
    status: "valid" | "moved" | "canceled";
 | 
					  status: "valid" | "moved" | "canceled";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRemote {
 | 
					export interface CalendarRemote {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    endDate: DateTime;
 | 
					  endDate: DateTime;
 | 
				
			||||||
    startDate: DateTime;
 | 
					  startDate: DateTime;
 | 
				
			||||||
    title: string;
 | 
					  title: string;
 | 
				
			||||||
    isAllDay: boolean;
 | 
					  isAllDay: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type EventInputCalendarRange = EventInput & {
 | 
					export type EventInputCalendarRange = EventInput & {
 | 
				
			||||||
    id: string;
 | 
					  id: string;
 | 
				
			||||||
    userId: number;
 | 
					  userId: number;
 | 
				
			||||||
    userLabel: string;
 | 
					  userLabel: string;
 | 
				
			||||||
    calendarRangeId: number;
 | 
					  calendarRangeId: number;
 | 
				
			||||||
    locationId: number;
 | 
					  locationId: number;
 | 
				
			||||||
    locationName: string;
 | 
					  locationName: string;
 | 
				
			||||||
    start: string;
 | 
					  start: string;
 | 
				
			||||||
    end: string;
 | 
					  end: string;
 | 
				
			||||||
    is: "range";
 | 
					  is: "range";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function isEventInputCalendarRange(
 | 
					export function isEventInputCalendarRange(
 | 
				
			||||||
    toBeDetermined: EventInputCalendarRange | EventInput,
 | 
					  toBeDetermined: EventInputCalendarRange | EventInput,
 | 
				
			||||||
): toBeDetermined is EventInputCalendarRange {
 | 
					): toBeDetermined is EventInputCalendarRange {
 | 
				
			||||||
    return (
 | 
					  return typeof toBeDetermined.is === "string" && toBeDetermined.is === "range";
 | 
				
			||||||
        typeof toBeDetermined.is === "string" && toBeDetermined.is === "range"
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {};
 | 
					export {};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,164 +1,146 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <teleport to="#mainUser">
 | 
					  <teleport to="#mainUser">
 | 
				
			||||||
        <h2 class="chill-red">Utilisateur principal</h2>
 | 
					    <h2 class="chill-red">Utilisateur principal</h2>
 | 
				
			||||||
        <div>
 | 
					    <div>
 | 
				
			||||||
            <div>
 | 
					      <div>
 | 
				
			||||||
                <div v-if="null !== this.$store.getters.getMainUser">
 | 
					        <div v-if="null !== this.$store.getters.getMainUser">
 | 
				
			||||||
                    <calendar-active :user="this.$store.getters.getMainUser" />
 | 
					          <calendar-active :user="this.$store.getters.getMainUser" />
 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <pick-entity
 | 
					 | 
				
			||||||
                    :multiple="false"
 | 
					 | 
				
			||||||
                    :types="['user']"
 | 
					 | 
				
			||||||
                    :uniqid="'main_user_calendar'"
 | 
					 | 
				
			||||||
                    :picked="
 | 
					 | 
				
			||||||
                        null !== this.$store.getters.getMainUser
 | 
					 | 
				
			||||||
                            ? [this.$store.getters.getMainUser]
 | 
					 | 
				
			||||||
                            : []
 | 
					 | 
				
			||||||
                    "
 | 
					 | 
				
			||||||
                    :removable-if-set="false"
 | 
					 | 
				
			||||||
                    :display-picked="false"
 | 
					 | 
				
			||||||
                    :suggested="this.suggestedUsers"
 | 
					 | 
				
			||||||
                    :label="'main_user'"
 | 
					 | 
				
			||||||
                    @add-new-entity="setMainUser"
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </teleport>
 | 
					        <pick-entity
 | 
				
			||||||
 | 
					          :multiple="false"
 | 
				
			||||||
 | 
					          :types="['user']"
 | 
				
			||||||
 | 
					          :uniqid="'main_user_calendar'"
 | 
				
			||||||
 | 
					          :picked="
 | 
				
			||||||
 | 
					            null !== this.$store.getters.getMainUser
 | 
				
			||||||
 | 
					              ? [this.$store.getters.getMainUser]
 | 
				
			||||||
 | 
					              : []
 | 
				
			||||||
 | 
					          "
 | 
				
			||||||
 | 
					          :removable-if-set="false"
 | 
				
			||||||
 | 
					          :display-picked="false"
 | 
				
			||||||
 | 
					          :suggested="this.suggestedUsers"
 | 
				
			||||||
 | 
					          :label="'main_user'"
 | 
				
			||||||
 | 
					          @add-new-entity="setMainUser"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </teleport>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <concerned-groups />
 | 
					  <concerned-groups />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <teleport to="#schedule">
 | 
					  <teleport to="#schedule">
 | 
				
			||||||
        <div class="row mb-3" v-if="activity.startDate !== null">
 | 
					    <div class="row mb-3" v-if="activity.startDate !== null">
 | 
				
			||||||
            <label class="col-form-label col-sm-4">Date</label>
 | 
					      <label class="col-form-label col-sm-4">Date</label>
 | 
				
			||||||
            <div class="col-sm-8">
 | 
					      <div class="col-sm-8">
 | 
				
			||||||
                {{ $d(activity.startDate, "long") }} -
 | 
					        {{ $d(activity.startDate, "long") }} -
 | 
				
			||||||
                {{ $d(activity.endDate, "hoursOnly") }}
 | 
					        {{ $d(activity.endDate, "hoursOnly") }}
 | 
				
			||||||
                <span v-if="activity.calendarRange === null"
 | 
					        <span v-if="activity.calendarRange === null"
 | 
				
			||||||
                    >(Pas de plage de disponibilité sélectionnée)</span
 | 
					          >(Pas de plage de disponibilité sélectionnée)</span
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                <span v-else>(Une plage de disponibilité sélectionnée)</span>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </teleport>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <location />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <teleport to="#fullCalendar">
 | 
					 | 
				
			||||||
        <div class="calendar-actives">
 | 
					 | 
				
			||||||
            <template v-for="u in getActiveUsers" :key="u.id">
 | 
					 | 
				
			||||||
                <calendar-active
 | 
					 | 
				
			||||||
                    :user="u"
 | 
					 | 
				
			||||||
                    :invite="this.$store.getters.getInviteForUser(u)"
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
            </template>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div
 | 
					 | 
				
			||||||
            class="display-options row justify-content-between"
 | 
					 | 
				
			||||||
            style="margin-top: 1rem"
 | 
					 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <div class="col-sm-9 col-xs-12">
 | 
					        <span v-else>(Une plage de disponibilité sélectionnée)</span>
 | 
				
			||||||
                <div class="input-group mb-3">
 | 
					      </div>
 | 
				
			||||||
                    <label class="input-group-text" for="slotDuration"
 | 
					    </div>
 | 
				
			||||||
                        >Durée des créneaux</label
 | 
					  </teleport>
 | 
				
			||||||
                    >
 | 
					
 | 
				
			||||||
                    <select
 | 
					  <location />
 | 
				
			||||||
                        v-model="slotDuration"
 | 
					
 | 
				
			||||||
                        id="slotDuration"
 | 
					  <teleport to="#fullCalendar">
 | 
				
			||||||
                        class="form-select"
 | 
					    <div class="calendar-actives">
 | 
				
			||||||
                    >
 | 
					      <template v-for="u in getActiveUsers" :key="u.id">
 | 
				
			||||||
                        <option value="00:05:00">5 minutes</option>
 | 
					        <calendar-active
 | 
				
			||||||
                        <option value="00:10:00">10 minutes</option>
 | 
					          :user="u"
 | 
				
			||||||
                        <option value="00:15:00">15 minutes</option>
 | 
					          :invite="this.$store.getters.getInviteForUser(u)"
 | 
				
			||||||
                        <option value="00:30:00">30 minutes</option>
 | 
					        />
 | 
				
			||||||
                    </select>
 | 
					      </template>
 | 
				
			||||||
                    <label class="input-group-text" for="slotMinTime">De</label>
 | 
					    </div>
 | 
				
			||||||
                    <select
 | 
					    <div
 | 
				
			||||||
                        v-model="slotMinTime"
 | 
					      class="display-options row justify-content-between"
 | 
				
			||||||
                        id="slotMinTime"
 | 
					      style="margin-top: 1rem"
 | 
				
			||||||
                        class="form-select"
 | 
					    >
 | 
				
			||||||
                    >
 | 
					      <div class="col-sm-9 col-xs-12">
 | 
				
			||||||
                        <option value="00:00:00">0h</option>
 | 
					        <div class="input-group mb-3">
 | 
				
			||||||
                        <option value="01:00:00">1h</option>
 | 
					          <label class="input-group-text" for="slotDuration"
 | 
				
			||||||
                        <option value="02:00:00">2h</option>
 | 
					            >Durée des créneaux</label
 | 
				
			||||||
                        <option value="03:00:00">3h</option>
 | 
					          >
 | 
				
			||||||
                        <option value="04:00:00">4h</option>
 | 
					          <select v-model="slotDuration" id="slotDuration" class="form-select">
 | 
				
			||||||
                        <option value="05:00:00">5h</option>
 | 
					            <option value="00:05:00">5 minutes</option>
 | 
				
			||||||
                        <option value="06:00:00">6h</option>
 | 
					            <option value="00:10:00">10 minutes</option>
 | 
				
			||||||
                        <option value="07:00:00">7h</option>
 | 
					            <option value="00:15:00">15 minutes</option>
 | 
				
			||||||
                        <option value="08:00:00">8h</option>
 | 
					            <option value="00:30:00">30 minutes</option>
 | 
				
			||||||
                        <option value="09:00:00">9h</option>
 | 
					          </select>
 | 
				
			||||||
                        <option value="10:00:00">10h</option>
 | 
					          <label class="input-group-text" for="slotMinTime">De</label>
 | 
				
			||||||
                        <option value="11:00:00">11h</option>
 | 
					          <select v-model="slotMinTime" id="slotMinTime" class="form-select">
 | 
				
			||||||
                        <option value="12:00:00">12h</option>
 | 
					            <option value="00:00:00">0h</option>
 | 
				
			||||||
                    </select>
 | 
					            <option value="01:00:00">1h</option>
 | 
				
			||||||
                    <label class="input-group-text" for="slotMaxTime">À</label>
 | 
					            <option value="02:00:00">2h</option>
 | 
				
			||||||
                    <select
 | 
					            <option value="03:00:00">3h</option>
 | 
				
			||||||
                        v-model="slotMaxTime"
 | 
					            <option value="04:00:00">4h</option>
 | 
				
			||||||
                        id="slotMaxTime"
 | 
					            <option value="05:00:00">5h</option>
 | 
				
			||||||
                        class="form-select"
 | 
					            <option value="06:00:00">6h</option>
 | 
				
			||||||
                    >
 | 
					            <option value="07:00:00">7h</option>
 | 
				
			||||||
                        <option value="12:00:00">12h</option>
 | 
					            <option value="08:00:00">8h</option>
 | 
				
			||||||
                        <option value="13:00:00">13h</option>
 | 
					            <option value="09:00:00">9h</option>
 | 
				
			||||||
                        <option value="14:00:00">14h</option>
 | 
					            <option value="10:00:00">10h</option>
 | 
				
			||||||
                        <option value="15:00:00">15h</option>
 | 
					            <option value="11:00:00">11h</option>
 | 
				
			||||||
                        <option value="16:00:00">16h</option>
 | 
					            <option value="12:00:00">12h</option>
 | 
				
			||||||
                        <option value="17:00:00">17h</option>
 | 
					          </select>
 | 
				
			||||||
                        <option value="18:00:00">18h</option>
 | 
					          <label class="input-group-text" for="slotMaxTime">À</label>
 | 
				
			||||||
                        <option value="19:00:00">19h</option>
 | 
					          <select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
 | 
				
			||||||
                        <option value="20:00:00">20h</option>
 | 
					            <option value="12:00:00">12h</option>
 | 
				
			||||||
                        <option value="21:00:00">21h</option>
 | 
					            <option value="13:00:00">13h</option>
 | 
				
			||||||
                        <option value="22:00:00">22h</option>
 | 
					            <option value="14:00:00">14h</option>
 | 
				
			||||||
                        <option value="23:00:00">23h</option>
 | 
					            <option value="15:00:00">15h</option>
 | 
				
			||||||
                        <option value="23:59:59">24h</option>
 | 
					            <option value="16:00:00">16h</option>
 | 
				
			||||||
                    </select>
 | 
					            <option value="17:00:00">17h</option>
 | 
				
			||||||
                </div>
 | 
					            <option value="18:00:00">18h</option>
 | 
				
			||||||
            </div>
 | 
					            <option value="19:00:00">19h</option>
 | 
				
			||||||
            <div class="col-sm-3 col-xs-12">
 | 
					            <option value="20:00:00">20h</option>
 | 
				
			||||||
                <div class="float-end">
 | 
					            <option value="21:00:00">21h</option>
 | 
				
			||||||
                    <div class="form-check input-group">
 | 
					            <option value="22:00:00">22h</option>
 | 
				
			||||||
                        <span class="input-group-text">
 | 
					            <option value="23:00:00">23h</option>
 | 
				
			||||||
                            <input
 | 
					            <option value="23:59:59">24h</option>
 | 
				
			||||||
                                id="showHideWE"
 | 
					          </select>
 | 
				
			||||||
                                class="mt-0"
 | 
					 | 
				
			||||||
                                type="checkbox"
 | 
					 | 
				
			||||||
                                v-model="hideWeekends"
 | 
					 | 
				
			||||||
                            />
 | 
					 | 
				
			||||||
                        </span>
 | 
					 | 
				
			||||||
                        <label
 | 
					 | 
				
			||||||
                            for="showHideWE"
 | 
					 | 
				
			||||||
                            class="form-check-label input-group-text"
 | 
					 | 
				
			||||||
                            >Week-ends</label
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <FullCalendar ref="fullCalendar" :options="calendarOptions">
 | 
					      </div>
 | 
				
			||||||
            <template #eventContent="arg">
 | 
					      <div class="col-sm-3 col-xs-12">
 | 
				
			||||||
                <span>
 | 
					        <div class="float-end">
 | 
				
			||||||
                    <b v-if="arg.event.extendedProps.is === 'remote'">{{
 | 
					          <div class="form-check input-group">
 | 
				
			||||||
                        arg.event.title
 | 
					            <span class="input-group-text">
 | 
				
			||||||
                    }}</b>
 | 
					              <input
 | 
				
			||||||
                    <b v-else-if="arg.event.extendedProps.is === 'range'"
 | 
					                id="showHideWE"
 | 
				
			||||||
                        >{{ arg.timeText }}
 | 
					                class="mt-0"
 | 
				
			||||||
                        {{ arg.event.extendedProps.locationName }}
 | 
					                type="checkbox"
 | 
				
			||||||
                        <small>{{
 | 
					                v-model="hideWeekends"
 | 
				
			||||||
                            arg.event.extendedProps.userLabel
 | 
					              />
 | 
				
			||||||
                        }}</small></b
 | 
					            </span>
 | 
				
			||||||
                    >
 | 
					            <label for="showHideWE" class="form-check-label input-group-text"
 | 
				
			||||||
                    <b v-else-if="arg.event.extendedProps.is === 'current'"
 | 
					              >Week-ends</label
 | 
				
			||||||
                        >{{ arg.timeText }} {{ $t("current_selected") }}
 | 
					            >
 | 
				
			||||||
                    </b>
 | 
					          </div>
 | 
				
			||||||
                    <b v-else-if="arg.event.extendedProps.is === 'local'">{{
 | 
					        </div>
 | 
				
			||||||
                        arg.event.title
 | 
					      </div>
 | 
				
			||||||
                    }}</b>
 | 
					    </div>
 | 
				
			||||||
                    <b v-else
 | 
					    <FullCalendar ref="fullCalendar" :options="calendarOptions">
 | 
				
			||||||
                        >{{ arg.timeText }} {{ $t("current_selected") }}
 | 
					      <template #eventContent="arg">
 | 
				
			||||||
                    </b>
 | 
					        <span>
 | 
				
			||||||
                </span>
 | 
					          <b v-if="arg.event.extendedProps.is === 'remote'">{{
 | 
				
			||||||
            </template>
 | 
					            arg.event.title
 | 
				
			||||||
        </FullCalendar>
 | 
					          }}</b>
 | 
				
			||||||
    </teleport>
 | 
					          <b v-else-if="arg.event.extendedProps.is === 'range'"
 | 
				
			||||||
 | 
					            >{{ arg.timeText }}
 | 
				
			||||||
 | 
					            {{ arg.event.extendedProps.locationName }}
 | 
				
			||||||
 | 
					            <small>{{ arg.event.extendedProps.userLabel }}</small></b
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					          <b v-else-if="arg.event.extendedProps.is === 'current'"
 | 
				
			||||||
 | 
					            >{{ arg.timeText }} {{ $t("current_selected") }}
 | 
				
			||||||
 | 
					          </b>
 | 
				
			||||||
 | 
					          <b v-else-if="arg.event.extendedProps.is === 'local'">{{
 | 
				
			||||||
 | 
					            arg.event.title
 | 
				
			||||||
 | 
					          }}</b>
 | 
				
			||||||
 | 
					          <b v-else>{{ arg.timeText }} {{ $t("current_selected") }} </b>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					    </FullCalendar>
 | 
				
			||||||
 | 
					  </teleport>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -175,219 +157,210 @@ import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
 | 
				
			|||||||
import { mapGetters, mapState } from "vuex";
 | 
					import { mapGetters, mapState } from "vuex";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "App",
 | 
					  name: "App",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        ConcernedGroups,
 | 
					    ConcernedGroups,
 | 
				
			||||||
        Location,
 | 
					    Location,
 | 
				
			||||||
        FullCalendar,
 | 
					    FullCalendar,
 | 
				
			||||||
        CalendarActive,
 | 
					    CalendarActive,
 | 
				
			||||||
        PickEntity,
 | 
					    PickEntity,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      errorMsg: [],
 | 
				
			||||||
 | 
					      showMyCalendar: false,
 | 
				
			||||||
 | 
					      slotDuration: "00:05:00",
 | 
				
			||||||
 | 
					      slotMinTime: "09:00:00",
 | 
				
			||||||
 | 
					      slotMaxTime: "18:00:00",
 | 
				
			||||||
 | 
					      hideWeekEnds: true,
 | 
				
			||||||
 | 
					      previousUser: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    ...mapGetters(["getMainUser"]),
 | 
				
			||||||
 | 
					    ...mapState(["activity"]),
 | 
				
			||||||
 | 
					    events() {
 | 
				
			||||||
 | 
					      return this.$store.getters.getEventSources;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					    calendarOptions() {
 | 
				
			||||||
        return {
 | 
					      return {
 | 
				
			||||||
            errorMsg: [],
 | 
					        locale: frLocale,
 | 
				
			||||||
            showMyCalendar: false,
 | 
					        plugins: [
 | 
				
			||||||
            slotDuration: "00:05:00",
 | 
					          dayGridPlugin,
 | 
				
			||||||
            slotMinTime: "09:00:00",
 | 
					          interactionPlugin,
 | 
				
			||||||
            slotMaxTime: "18:00:00",
 | 
					          timeGridPlugin,
 | 
				
			||||||
            hideWeekEnds: true,
 | 
					          dayGridPlugin,
 | 
				
			||||||
            previousUser: [],
 | 
					          listPlugin,
 | 
				
			||||||
        };
 | 
					        ],
 | 
				
			||||||
 | 
					        initialView: "timeGridWeek",
 | 
				
			||||||
 | 
					        initialDate: this.$store.getters.getInitialDate,
 | 
				
			||||||
 | 
					        eventSources: this.events,
 | 
				
			||||||
 | 
					        selectable: true,
 | 
				
			||||||
 | 
					        slotMinTime: this.slotMinTime,
 | 
				
			||||||
 | 
					        slotMaxTime: this.slotMaxTime,
 | 
				
			||||||
 | 
					        scrollTimeReset: false,
 | 
				
			||||||
 | 
					        datesSet: this.onDatesSet,
 | 
				
			||||||
 | 
					        select: this.onDateSelect,
 | 
				
			||||||
 | 
					        eventChange: this.onEventChange,
 | 
				
			||||||
 | 
					        eventClick: this.onEventClick,
 | 
				
			||||||
 | 
					        selectMirror: true,
 | 
				
			||||||
 | 
					        editable: true,
 | 
				
			||||||
 | 
					        weekends: !this.hideWeekEnds,
 | 
				
			||||||
 | 
					        headerToolbar: {
 | 
				
			||||||
 | 
					          left: "prev,next today",
 | 
				
			||||||
 | 
					          center: "title",
 | 
				
			||||||
 | 
					          right: "timeGridWeek,timeGridDay,listWeek",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        views: {
 | 
				
			||||||
 | 
					          timeGrid: {
 | 
				
			||||||
 | 
					            slotEventOverlap: false,
 | 
				
			||||||
 | 
					            slotDuration: this.slotDuration,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    getActiveUsers() {
 | 
				
			||||||
        ...mapGetters(["getMainUser"]),
 | 
					      const users = [];
 | 
				
			||||||
        ...mapState(["activity"]),
 | 
					      for (const id of this.$store.state.currentView.users.keys()) {
 | 
				
			||||||
        events() {
 | 
					        users.push(this.$store.getters.getUserDataById(id).user);
 | 
				
			||||||
            return this.$store.getters.getEventSources;
 | 
					      }
 | 
				
			||||||
        },
 | 
					      return users;
 | 
				
			||||||
        calendarOptions() {
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                locale: frLocale,
 | 
					 | 
				
			||||||
                plugins: [
 | 
					 | 
				
			||||||
                    dayGridPlugin,
 | 
					 | 
				
			||||||
                    interactionPlugin,
 | 
					 | 
				
			||||||
                    timeGridPlugin,
 | 
					 | 
				
			||||||
                    dayGridPlugin,
 | 
					 | 
				
			||||||
                    listPlugin,
 | 
					 | 
				
			||||||
                ],
 | 
					 | 
				
			||||||
                initialView: "timeGridWeek",
 | 
					 | 
				
			||||||
                initialDate: this.$store.getters.getInitialDate,
 | 
					 | 
				
			||||||
                eventSources: this.events,
 | 
					 | 
				
			||||||
                selectable: true,
 | 
					 | 
				
			||||||
                slotMinTime: this.slotMinTime,
 | 
					 | 
				
			||||||
                slotMaxTime: this.slotMaxTime,
 | 
					 | 
				
			||||||
                scrollTimeReset: false,
 | 
					 | 
				
			||||||
                datesSet: this.onDatesSet,
 | 
					 | 
				
			||||||
                select: this.onDateSelect,
 | 
					 | 
				
			||||||
                eventChange: this.onEventChange,
 | 
					 | 
				
			||||||
                eventClick: this.onEventClick,
 | 
					 | 
				
			||||||
                selectMirror: true,
 | 
					 | 
				
			||||||
                editable: true,
 | 
					 | 
				
			||||||
                weekends: !this.hideWeekEnds,
 | 
					 | 
				
			||||||
                headerToolbar: {
 | 
					 | 
				
			||||||
                    left: "prev,next today",
 | 
					 | 
				
			||||||
                    center: "title",
 | 
					 | 
				
			||||||
                    right: "timeGridWeek,timeGridDay,listWeek",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                views: {
 | 
					 | 
				
			||||||
                    timeGrid: {
 | 
					 | 
				
			||||||
                        slotEventOverlap: false,
 | 
					 | 
				
			||||||
                        slotDuration: this.slotDuration,
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        getActiveUsers() {
 | 
					 | 
				
			||||||
            const users = [];
 | 
					 | 
				
			||||||
            for (const id of this.$store.state.currentView.users.keys()) {
 | 
					 | 
				
			||||||
                users.push(this.$store.getters.getUserDataById(id).user);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return users;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        suggestedUsers() {
 | 
					 | 
				
			||||||
            const suggested = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.$data.previousUser.forEach((u) => {
 | 
					 | 
				
			||||||
                if (u.id !== this.$store.getters.getMainUser.id) {
 | 
					 | 
				
			||||||
                    suggested.push(u);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return suggested;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    suggestedUsers() {
 | 
				
			||||||
        setMainUser({ entity }) {
 | 
					      const suggested = [];
 | 
				
			||||||
            const user = entity;
 | 
					 | 
				
			||||||
            console.log("setMainUser APP", entity);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (
 | 
					      this.$data.previousUser.forEach((u) => {
 | 
				
			||||||
                user.id !== this.$store.getters.getMainUser &&
 | 
					        if (u.id !== this.$store.getters.getMainUser.id) {
 | 
				
			||||||
                (this.$store.state.activity.calendarRange !== null ||
 | 
					          suggested.push(u);
 | 
				
			||||||
                    this.$store.state.activity.startDate !== null ||
 | 
					        }
 | 
				
			||||||
                    this.$store.state.activity.endDate !== null)
 | 
					      });
 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    !window.confirm(
 | 
					 | 
				
			||||||
                        this.$t("change_main_user_will_reset_event_data"),
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // add the previous user, if any, in the previous user list (in use for suggestion)
 | 
					      return suggested;
 | 
				
			||||||
            if (null !== this.$store.getters.getMainUser) {
 | 
					 | 
				
			||||||
                const suggestedUids = new Set(
 | 
					 | 
				
			||||||
                    this.$data.previousUser.map((u) => u.id),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
 | 
					 | 
				
			||||||
                    this.$data.previousUser.push(
 | 
					 | 
				
			||||||
                        this.$store.getters.getMainUser,
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.$store.dispatch("setMainUser", user);
 | 
					 | 
				
			||||||
            this.$store.commit("showUserOnCalendar", {
 | 
					 | 
				
			||||||
                user,
 | 
					 | 
				
			||||||
                ranges: true,
 | 
					 | 
				
			||||||
                remotes: true,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        removeMainUser(user) {
 | 
					 | 
				
			||||||
            console.log("removeMainUser APP", user);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            window.alert(this.$t("main_user_is_mandatory"));
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onDatesSet(event) {
 | 
					 | 
				
			||||||
            console.log("onDatesSet", event);
 | 
					 | 
				
			||||||
            this.$store.dispatch("setCurrentDatesView", {
 | 
					 | 
				
			||||||
                start: event.start,
 | 
					 | 
				
			||||||
                end: event.end,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onDateSelect(payload) {
 | 
					 | 
				
			||||||
            console.log("onDateSelect", payload);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // show an alert if changing mainUser
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                (this.$store.getters.getMainUser !== null &&
 | 
					 | 
				
			||||||
                    this.$store.state.me.id !==
 | 
					 | 
				
			||||||
                        this.$store.getters.getMainUser.id) ||
 | 
					 | 
				
			||||||
                this.$store.getters.getMainUser === null
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    this.$store.commit("showUserOnCalendar", {
 | 
					 | 
				
			||||||
                        user: this.$store.state.me,
 | 
					 | 
				
			||||||
                        remotes: true,
 | 
					 | 
				
			||||||
                        ranges: true,
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.$store.dispatch("setEventTimes", {
 | 
					 | 
				
			||||||
                start: payload.start,
 | 
					 | 
				
			||||||
                end: payload.end,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onEventChange(payload) {
 | 
					 | 
				
			||||||
            console.log("onEventChange", payload);
 | 
					 | 
				
			||||||
            if (this.$store.state.activity.calendarRange !== null) {
 | 
					 | 
				
			||||||
                throw new Error(
 | 
					 | 
				
			||||||
                    "not allowed to edit a calendar associated with a calendar range",
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            this.$store.dispatch("setEventTimes", {
 | 
					 | 
				
			||||||
                start: payload.event.start,
 | 
					 | 
				
			||||||
                end: payload.event.end,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onEventClick(payload) {
 | 
					 | 
				
			||||||
            if (payload.event.extendedProps.is !== "range") {
 | 
					 | 
				
			||||||
                // do nothing when clicking on remote
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // show an alert if changing mainUser
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                this.$store.getters.getMainUser !== null &&
 | 
					 | 
				
			||||||
                payload.event.extendedProps.userId !==
 | 
					 | 
				
			||||||
                    this.$store.getters.getMainUser.id
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                if (
 | 
					 | 
				
			||||||
                    !window.confirm(
 | 
					 | 
				
			||||||
                        this.$t("this_calendar_range_will_change_main_user"),
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                ) {
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            this.$store.dispatch("associateCalendarToRange", {
 | 
					 | 
				
			||||||
                range: payload.event,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    setMainUser({ entity }) {
 | 
				
			||||||
 | 
					      const user = entity;
 | 
				
			||||||
 | 
					      console.log("setMainUser APP", entity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        user.id !== this.$store.getters.getMainUser &&
 | 
				
			||||||
 | 
					        (this.$store.state.activity.calendarRange !== null ||
 | 
				
			||||||
 | 
					          this.$store.state.activity.startDate !== null ||
 | 
				
			||||||
 | 
					          this.$store.state.activity.endDate !== null)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          !window.confirm(this.$t("change_main_user_will_reset_event_data"))
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // add the previous user, if any, in the previous user list (in use for suggestion)
 | 
				
			||||||
 | 
					      if (null !== this.$store.getters.getMainUser) {
 | 
				
			||||||
 | 
					        const suggestedUids = new Set(this.$data.previousUser.map((u) => u.id));
 | 
				
			||||||
 | 
					        if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
 | 
				
			||||||
 | 
					          this.$data.previousUser.push(this.$store.getters.getMainUser);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.$store.dispatch("setMainUser", user);
 | 
				
			||||||
 | 
					      this.$store.commit("showUserOnCalendar", {
 | 
				
			||||||
 | 
					        user,
 | 
				
			||||||
 | 
					        ranges: true,
 | 
				
			||||||
 | 
					        remotes: true,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    removeMainUser(user) {
 | 
				
			||||||
 | 
					      console.log("removeMainUser APP", user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      window.alert(this.$t("main_user_is_mandatory"));
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onDatesSet(event) {
 | 
				
			||||||
 | 
					      console.log("onDatesSet", event);
 | 
				
			||||||
 | 
					      this.$store.dispatch("setCurrentDatesView", {
 | 
				
			||||||
 | 
					        start: event.start,
 | 
				
			||||||
 | 
					        end: event.end,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onDateSelect(payload) {
 | 
				
			||||||
 | 
					      console.log("onDateSelect", payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // show an alert if changing mainUser
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        (this.$store.getters.getMainUser !== null &&
 | 
				
			||||||
 | 
					          this.$store.state.me.id !== this.$store.getters.getMainUser.id) ||
 | 
				
			||||||
 | 
					        this.$store.getters.getMainUser === null
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.$store.commit("showUserOnCalendar", {
 | 
				
			||||||
 | 
					            user: this.$store.state.me,
 | 
				
			||||||
 | 
					            remotes: true,
 | 
				
			||||||
 | 
					            ranges: true,
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.$store.dispatch("setEventTimes", {
 | 
				
			||||||
 | 
					        start: payload.start,
 | 
				
			||||||
 | 
					        end: payload.end,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onEventChange(payload) {
 | 
				
			||||||
 | 
					      console.log("onEventChange", payload);
 | 
				
			||||||
 | 
					      if (this.$store.state.activity.calendarRange !== null) {
 | 
				
			||||||
 | 
					        throw new Error(
 | 
				
			||||||
 | 
					          "not allowed to edit a calendar associated with a calendar range",
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.$store.dispatch("setEventTimes", {
 | 
				
			||||||
 | 
					        start: payload.event.start,
 | 
				
			||||||
 | 
					        end: payload.event.end,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onEventClick(payload) {
 | 
				
			||||||
 | 
					      if (payload.event.extendedProps.is !== "range") {
 | 
				
			||||||
 | 
					        // do nothing when clicking on remote
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // show an alert if changing mainUser
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        this.$store.getters.getMainUser !== null &&
 | 
				
			||||||
 | 
					        payload.event.extendedProps.userId !==
 | 
				
			||||||
 | 
					          this.$store.getters.getMainUser.id
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          !window.confirm(this.$t("this_calendar_range_will_change_main_user"))
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.$store.dispatch("associateCalendarToRange", {
 | 
				
			||||||
 | 
					        range: payload.event,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
.calendar-actives {
 | 
					.calendar-actives {
 | 
				
			||||||
    display: flex;
 | 
					  display: flex;
 | 
				
			||||||
    flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
    flex-wrap: wrap;
 | 
					  flex-wrap: wrap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.display-options {
 | 
					.display-options {
 | 
				
			||||||
    margin-top: 1rem;
 | 
					  margin-top: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* for events which are range */
 | 
					/* for events which are range */
 | 
				
			||||||
.fc-event.isrange {
 | 
					.fc-event.isrange {
 | 
				
			||||||
    border-width: 3px;
 | 
					  border-width: 3px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,119 +1,105 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div :style="style" class="calendar-active">
 | 
					  <div :style="style" class="calendar-active">
 | 
				
			||||||
        <span class="badge-user">
 | 
					    <span class="badge-user">
 | 
				
			||||||
            {{ user.text }}
 | 
					      {{ user.text }}
 | 
				
			||||||
            <template v-if="invite !== null">
 | 
					      <template v-if="invite !== null">
 | 
				
			||||||
                <i v-if="invite.status === 'accepted'" class="fa fa-check" />
 | 
					        <i v-if="invite.status === 'accepted'" class="fa fa-check" />
 | 
				
			||||||
                <i
 | 
					        <i v-else-if="invite.status === 'declined'" class="fa fa-times" />
 | 
				
			||||||
                    v-else-if="invite.status === 'declined'"
 | 
					        <i v-else-if="invite.status === 'pending'" class="fa fa-question-o" />
 | 
				
			||||||
                    class="fa fa-times"
 | 
					        <i v-else-if="invite.status === 'tentative'" class="fa fa-question" />
 | 
				
			||||||
                />
 | 
					        <span v-else="">{{ invite.status }}</span>
 | 
				
			||||||
                <i
 | 
					      </template>
 | 
				
			||||||
                    v-else-if="invite.status === 'pending'"
 | 
					    </span>
 | 
				
			||||||
                    class="fa fa-question-o"
 | 
					    <span class="form-check-inline form-switch">
 | 
				
			||||||
                />
 | 
					      <input
 | 
				
			||||||
                <i
 | 
					        class="form-check-input"
 | 
				
			||||||
                    v-else-if="invite.status === 'tentative'"
 | 
					        type="checkbox"
 | 
				
			||||||
                    class="fa fa-question"
 | 
					        id="flexSwitchCheckDefault"
 | 
				
			||||||
                />
 | 
					        v-model="rangeShow"
 | 
				
			||||||
                <span v-else="">{{ invite.status }}</span>
 | 
					      />
 | 
				
			||||||
            </template>
 | 
					       <label
 | 
				
			||||||
        </span>
 | 
					        class="form-check-label"
 | 
				
			||||||
        <span class="form-check-inline form-switch">
 | 
					        for="flexSwitchCheckDefault"
 | 
				
			||||||
            <input
 | 
					        title="Disponibilités"
 | 
				
			||||||
                class="form-check-input"
 | 
					        ><i class="fa fa-calendar-check-o"
 | 
				
			||||||
                type="checkbox"
 | 
					      /></label>
 | 
				
			||||||
                id="flexSwitchCheckDefault"
 | 
					    </span>
 | 
				
			||||||
                v-model="rangeShow"
 | 
					    <span class="form-check-inline form-switch">
 | 
				
			||||||
            />
 | 
					      <input
 | 
				
			||||||
             <label
 | 
					        class="form-check-input"
 | 
				
			||||||
                class="form-check-label"
 | 
					        type="checkbox"
 | 
				
			||||||
                for="flexSwitchCheckDefault"
 | 
					        id="flexSwitchCheckDefault"
 | 
				
			||||||
                title="Disponibilités"
 | 
					        v-model="remoteShow"
 | 
				
			||||||
                ><i class="fa fa-calendar-check-o"
 | 
					      />
 | 
				
			||||||
            /></label>
 | 
					       <label
 | 
				
			||||||
        </span>
 | 
					        class="form-check-label"
 | 
				
			||||||
        <span class="form-check-inline form-switch">
 | 
					        for="flexSwitchCheckDefault"
 | 
				
			||||||
            <input
 | 
					        title="Agenda"
 | 
				
			||||||
                class="form-check-input"
 | 
					        ><i class="fa fa-calendar"
 | 
				
			||||||
                type="checkbox"
 | 
					      /></label>
 | 
				
			||||||
                id="flexSwitchCheckDefault"
 | 
					    </span>
 | 
				
			||||||
                v-model="remoteShow"
 | 
					  </div>
 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
             <label
 | 
					 | 
				
			||||||
                class="form-check-label"
 | 
					 | 
				
			||||||
                for="flexSwitchCheckDefault"
 | 
					 | 
				
			||||||
                title="Agenda"
 | 
					 | 
				
			||||||
                ><i class="fa fa-calendar"
 | 
					 | 
				
			||||||
            /></label>
 | 
					 | 
				
			||||||
        </span>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { mapGetters } from "vuex";
 | 
					import { mapGetters } from "vuex";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "CalendarActive",
 | 
					  name: "CalendarActive",
 | 
				
			||||||
    props: {
 | 
					  props: {
 | 
				
			||||||
        user: {
 | 
					    user: {
 | 
				
			||||||
            type: Object,
 | 
					      type: Object,
 | 
				
			||||||
            required: true,
 | 
					      required: true,
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        invite: {
 | 
					 | 
				
			||||||
            type: Object,
 | 
					 | 
				
			||||||
            required: false,
 | 
					 | 
				
			||||||
            default: null,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    invite: {
 | 
				
			||||||
        style() {
 | 
					      type: Object,
 | 
				
			||||||
            return {
 | 
					      required: false,
 | 
				
			||||||
                backgroundColor: this.$store.getters.getUserData(this.user)
 | 
					      default: null,
 | 
				
			||||||
                    .mainColor,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        rangeShow: {
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.$store.commit("showUserOnCalendar", {
 | 
					 | 
				
			||||||
                    user: this.user,
 | 
					 | 
				
			||||||
                    ranges: value,
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.$store.getters.isRangeShownOnCalendarForUser(
 | 
					 | 
				
			||||||
                    this.user,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        remoteShow: {
 | 
					 | 
				
			||||||
            set(value) {
 | 
					 | 
				
			||||||
                this.$store.commit("showUserOnCalendar", {
 | 
					 | 
				
			||||||
                    user: this.user,
 | 
					 | 
				
			||||||
                    remotes: value,
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.$store.getters.isRemoteShownOnCalendarForUser(
 | 
					 | 
				
			||||||
                    this.user,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    style() {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        backgroundColor: this.$store.getters.getUserData(this.user).mainColor,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    rangeShow: {
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.$store.commit("showUserOnCalendar", {
 | 
				
			||||||
 | 
					          user: this.user,
 | 
				
			||||||
 | 
					          ranges: value,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.$store.getters.isRangeShownOnCalendarForUser(this.user);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    remoteShow: {
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.$store.commit("showUserOnCalendar", {
 | 
				
			||||||
 | 
					          user: this.user,
 | 
				
			||||||
 | 
					          remotes: value,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.$store.getters.isRemoteShownOnCalendarForUser(this.user);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
.calendar-active {
 | 
					.calendar-active {
 | 
				
			||||||
    margin: 0 0.25rem 0.25rem 0;
 | 
					  margin: 0 0.25rem 0.25rem 0;
 | 
				
			||||||
    padding: 0.5rem;
 | 
					  padding: 0.5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    border-radius: 0.5rem;
 | 
					  border-radius: 0.5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    color: var(--bs-blue);
 | 
					  color: var(--bs-blue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > .badge-user {
 | 
					  & > .badge-user {
 | 
				
			||||||
        margin-right: 0.5rem;
 | 
					    margin-right: 0.5rem;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,37 +14,37 @@ export { whoami } from "../../../../../ChillMainBundle/Resources/public/lib/api/
 | 
				
			|||||||
 * @return Promise
 | 
					 * @return Promise
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const fetchCalendarRangeForUser = (
 | 
					export const fetchCalendarRangeForUser = (
 | 
				
			||||||
    user: User,
 | 
					  user: User,
 | 
				
			||||||
    start: Date,
 | 
					  start: Date,
 | 
				
			||||||
    end: Date,
 | 
					  end: Date,
 | 
				
			||||||
): Promise<CalendarRange[]> => {
 | 
					): Promise<CalendarRange[]> => {
 | 
				
			||||||
    const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
 | 
					  const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
 | 
				
			||||||
    const dateFrom = datetimeToISO(start);
 | 
					  const dateFrom = datetimeToISO(start);
 | 
				
			||||||
    const dateTo = datetimeToISO(end);
 | 
					  const dateTo = datetimeToISO(end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return fetchResults<CalendarRange>(uri, { dateFrom, dateTo });
 | 
					  return fetchResults<CalendarRange>(uri, { dateFrom, dateTo });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const fetchCalendarRemoteForUser = (
 | 
					export const fetchCalendarRemoteForUser = (
 | 
				
			||||||
    user: User,
 | 
					  user: User,
 | 
				
			||||||
    start: Date,
 | 
					  start: Date,
 | 
				
			||||||
    end: Date,
 | 
					  end: Date,
 | 
				
			||||||
): Promise<CalendarRemote[]> => {
 | 
					): Promise<CalendarRemote[]> => {
 | 
				
			||||||
    const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
 | 
					  const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
 | 
				
			||||||
    const dateFrom = datetimeToISO(start);
 | 
					  const dateFrom = datetimeToISO(start);
 | 
				
			||||||
    const dateTo = datetimeToISO(end);
 | 
					  const dateTo = datetimeToISO(end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return fetchResults<CalendarRemote>(uri, { dateFrom, dateTo });
 | 
					  return fetchResults<CalendarRemote>(uri, { dateFrom, dateTo });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const fetchCalendarLocalForUser = (
 | 
					export const fetchCalendarLocalForUser = (
 | 
				
			||||||
    user: User,
 | 
					  user: User,
 | 
				
			||||||
    start: Date,
 | 
					  start: Date,
 | 
				
			||||||
    end: Date,
 | 
					  end: Date,
 | 
				
			||||||
): Promise<CalendarLight[]> => {
 | 
					): Promise<CalendarLight[]> => {
 | 
				
			||||||
    const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
 | 
					  const uri = `/api/1.0/calendar/calendar/by-user/${user.id}.json`;
 | 
				
			||||||
    const dateFrom = datetimeToISO(start);
 | 
					  const dateFrom = datetimeToISO(start);
 | 
				
			||||||
    const dateTo = datetimeToISO(end);
 | 
					  const dateTo = datetimeToISO(end);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return fetchResults<CalendarLight>(uri, { dateFrom, dateTo });
 | 
					  return fetchResults<CalendarLight>(uri, { dateFrom, dateTo });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
const COLORS = [
 | 
					const COLORS = [
 | 
				
			||||||
    /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
 | 
					  /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
 | 
				
			||||||
    "#8dd3c7",
 | 
					  "#8dd3c7",
 | 
				
			||||||
    "#ffffb3",
 | 
					  "#ffffb3",
 | 
				
			||||||
    "#bebada",
 | 
					  "#bebada",
 | 
				
			||||||
    "#fb8072",
 | 
					  "#fb8072",
 | 
				
			||||||
    "#80b1d3",
 | 
					  "#80b1d3",
 | 
				
			||||||
    "#fdb462",
 | 
					  "#fdb462",
 | 
				
			||||||
    "#b3de69",
 | 
					  "#b3de69",
 | 
				
			||||||
    "#fccde5",
 | 
					  "#fccde5",
 | 
				
			||||||
    "#d9d9d9",
 | 
					  "#d9d9d9",
 | 
				
			||||||
    "#bc80bd",
 | 
					  "#bc80bd",
 | 
				
			||||||
    "#ccebc5",
 | 
					  "#ccebc5",
 | 
				
			||||||
    "#ffed6f",
 | 
					  "#ffed6f",
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { COLORS };
 | 
					export { COLORS };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,117 +1,117 @@
 | 
				
			|||||||
import { COLORS } from "../const";
 | 
					import { COLORS } from "../const";
 | 
				
			||||||
import { ISOToDatetime } from "../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
					import { ISOToDatetime } from "../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    DateTime,
 | 
					  DateTime,
 | 
				
			||||||
    User,
 | 
					  User,
 | 
				
			||||||
} from "../../../../../../ChillMainBundle/Resources/public/types";
 | 
					} from "../../../../../../ChillMainBundle/Resources/public/types";
 | 
				
			||||||
import { CalendarLight, CalendarRange, CalendarRemote } from "../../../types";
 | 
					import { CalendarLight, CalendarRange, CalendarRemote } from "../../../types";
 | 
				
			||||||
import type { EventInputCalendarRange } from "../../../types";
 | 
					import type { EventInputCalendarRange } from "../../../types";
 | 
				
			||||||
import { EventInput } from "@fullcalendar/core";
 | 
					import { EventInput } from "@fullcalendar/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface UserData {
 | 
					export interface UserData {
 | 
				
			||||||
    user: User;
 | 
					  user: User;
 | 
				
			||||||
    calendarRanges: CalendarRange[];
 | 
					  calendarRanges: CalendarRange[];
 | 
				
			||||||
    calendarRangesLoaded: {}[];
 | 
					  calendarRangesLoaded: {}[];
 | 
				
			||||||
    remotes: CalendarRemote[];
 | 
					  remotes: CalendarRemote[];
 | 
				
			||||||
    remotesLoaded: {}[];
 | 
					  remotesLoaded: {}[];
 | 
				
			||||||
    locals: CalendarRemote[];
 | 
					  locals: CalendarRemote[];
 | 
				
			||||||
    localsLoaded: {}[];
 | 
					  localsLoaded: {}[];
 | 
				
			||||||
    mainColor: string;
 | 
					  mainColor: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addIdToValue = (string: string, id: number): string => {
 | 
					export const addIdToValue = (string: string, id: number): string => {
 | 
				
			||||||
    const array = string ? string.split(",") : [];
 | 
					  const array = string ? string.split(",") : [];
 | 
				
			||||||
    array.push(id.toString());
 | 
					  array.push(id.toString());
 | 
				
			||||||
    const str = array.join();
 | 
					  const str = array.join();
 | 
				
			||||||
    return str;
 | 
					  return str;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const removeIdFromValue = (string: string, id: number) => {
 | 
					export const removeIdFromValue = (string: string, id: number) => {
 | 
				
			||||||
    let array = string.split(",");
 | 
					  let array = string.split(",");
 | 
				
			||||||
    array = array.filter((el) => el !== id.toString());
 | 
					  array = array.filter((el) => el !== id.toString());
 | 
				
			||||||
    const str = array.join();
 | 
					  const str = array.join();
 | 
				
			||||||
    return str;
 | 
					  return str;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Assign missing keys for the ConcernedGroups component
 | 
					 * Assign missing keys for the ConcernedGroups component
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const mapEntity = (entity: EventInput): EventInput => {
 | 
					export const mapEntity = (entity: EventInput): EventInput => {
 | 
				
			||||||
    const calendar = { ...entity };
 | 
					  const calendar = { ...entity };
 | 
				
			||||||
    Object.assign(calendar, { thirdParties: entity.professionals });
 | 
					  Object.assign(calendar, { thirdParties: entity.professionals });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (entity.startDate !== null) {
 | 
					  if (entity.startDate !== null) {
 | 
				
			||||||
        calendar.startDate = ISOToDatetime(entity.startDate.datetime);
 | 
					    calendar.startDate = ISOToDatetime(entity.startDate.datetime);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (entity.endDate !== null) {
 | 
					  if (entity.endDate !== null) {
 | 
				
			||||||
        calendar.endDate = ISOToDatetime(entity.endDate.datetime);
 | 
					    calendar.endDate = ISOToDatetime(entity.endDate.datetime);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (entity.calendarRange !== null) {
 | 
					  if (entity.calendarRange !== null) {
 | 
				
			||||||
        calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
 | 
					    calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
 | 
				
			||||||
        calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
 | 
					    calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return calendar;
 | 
					  return calendar;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createUserData = (user: User, colorIndex: number): UserData => {
 | 
					export const createUserData = (user: User, colorIndex: number): UserData => {
 | 
				
			||||||
    const colorId = colorIndex % COLORS.length;
 | 
					  const colorId = colorIndex % COLORS.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
        user: user,
 | 
					    user: user,
 | 
				
			||||||
        calendarRanges: [],
 | 
					    calendarRanges: [],
 | 
				
			||||||
        calendarRangesLoaded: [],
 | 
					    calendarRangesLoaded: [],
 | 
				
			||||||
        remotes: [],
 | 
					    remotes: [],
 | 
				
			||||||
        remotesLoaded: [],
 | 
					    remotesLoaded: [],
 | 
				
			||||||
        locals: [],
 | 
					    locals: [],
 | 
				
			||||||
        localsLoaded: [],
 | 
					    localsLoaded: [],
 | 
				
			||||||
        mainColor: COLORS[colorId],
 | 
					    mainColor: COLORS[colorId],
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO move this function to a more global namespace, as it is also in use in MyCalendarRange app
 | 
					// TODO move this function to a more global namespace, as it is also in use in MyCalendarRange app
 | 
				
			||||||
export const calendarRangeToFullCalendarEvent = (
 | 
					export const calendarRangeToFullCalendarEvent = (
 | 
				
			||||||
    entity: CalendarRange,
 | 
					  entity: CalendarRange,
 | 
				
			||||||
): EventInputCalendarRange => {
 | 
					): EventInputCalendarRange => {
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
        id: `range_${entity.id}`,
 | 
					    id: `range_${entity.id}`,
 | 
				
			||||||
        title: "(" + entity.user.text + ")",
 | 
					    title: "(" + entity.user.text + ")",
 | 
				
			||||||
        start: entity.startDate.datetime8601,
 | 
					    start: entity.startDate.datetime8601,
 | 
				
			||||||
        end: entity.endDate.datetime8601,
 | 
					    end: entity.endDate.datetime8601,
 | 
				
			||||||
        allDay: false,
 | 
					    allDay: false,
 | 
				
			||||||
        userId: entity.user.id,
 | 
					    userId: entity.user.id,
 | 
				
			||||||
        userLabel: entity.user.label,
 | 
					    userLabel: entity.user.label,
 | 
				
			||||||
        calendarRangeId: entity.id,
 | 
					    calendarRangeId: entity.id,
 | 
				
			||||||
        locationId: entity.location.id,
 | 
					    locationId: entity.location.id,
 | 
				
			||||||
        locationName: entity.location.name,
 | 
					    locationName: entity.location.name,
 | 
				
			||||||
        is: "range",
 | 
					    is: "range",
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const remoteToFullCalendarEvent = (
 | 
					export const remoteToFullCalendarEvent = (
 | 
				
			||||||
    entity: CalendarRemote,
 | 
					  entity: CalendarRemote,
 | 
				
			||||||
): EventInput & { id: string } => {
 | 
					): EventInput & { id: string } => {
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
        id: `range_${entity.id}`,
 | 
					    id: `range_${entity.id}`,
 | 
				
			||||||
        title: entity.title,
 | 
					    title: entity.title,
 | 
				
			||||||
        start: entity.startDate.datetime8601,
 | 
					    start: entity.startDate.datetime8601,
 | 
				
			||||||
        end: entity.endDate.datetime8601,
 | 
					    end: entity.endDate.datetime8601,
 | 
				
			||||||
        allDay: entity.isAllDay,
 | 
					    allDay: entity.isAllDay,
 | 
				
			||||||
        is: "remote",
 | 
					    is: "remote",
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const localsToFullCalendarEvent = (
 | 
					export const localsToFullCalendarEvent = (
 | 
				
			||||||
    entity: CalendarLight,
 | 
					  entity: CalendarLight,
 | 
				
			||||||
): EventInput & { id: string; originId: number } => {
 | 
					): EventInput & { id: string; originId: number } => {
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
        id: `local_${entity.id}`,
 | 
					    id: `local_${entity.id}`,
 | 
				
			||||||
        title: entity.persons.map((p) => p.text).join(", "),
 | 
					    title: entity.persons.map((p) => p.text).join(", "),
 | 
				
			||||||
        originId: entity.id,
 | 
					    originId: entity.id,
 | 
				
			||||||
        start: entity.startDate.datetime8601,
 | 
					    start: entity.startDate.datetime8601,
 | 
				
			||||||
        end: entity.endDate.datetime8601,
 | 
					    end: entity.endDate.datetime8601,
 | 
				
			||||||
        allDay: false,
 | 
					    allDay: false,
 | 
				
			||||||
        is: "local",
 | 
					    is: "local",
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,58 +1,50 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="btn-group" role="group">
 | 
					  <div class="btn-group" role="group">
 | 
				
			||||||
        <button
 | 
					    <button
 | 
				
			||||||
            id="btnGroupDrop1"
 | 
					      id="btnGroupDrop1"
 | 
				
			||||||
            type="button"
 | 
					      type="button"
 | 
				
			||||||
            class="btn btn-misc dropdown-toggle"
 | 
					      class="btn btn-misc dropdown-toggle"
 | 
				
			||||||
            data-bs-toggle="dropdown"
 | 
					      data-bs-toggle="dropdown"
 | 
				
			||||||
            aria-expanded="false"
 | 
					      aria-expanded="false"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <template v-if="status === Statuses.PENDING">
 | 
				
			||||||
 | 
					        <span class="fa fa-hourglass"></span> {{ $t("Give_an_answer") }}
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					      <template v-else-if="status === Statuses.ACCEPTED">
 | 
				
			||||||
 | 
					        <span class="fa fa-check"></span> {{ $t("Accepted") }}
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					      <template v-else-if="status === Statuses.DECLINED">
 | 
				
			||||||
 | 
					        <span class="fa fa-times"></span> {{ $t("Declined") }}
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					      <template v-else-if="status === Statuses.TENTATIVELY_ACCEPTED">
 | 
				
			||||||
 | 
					        <span class="fa fa-question"></span> {{ $t("Tentative") }}
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					    <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
 | 
				
			||||||
 | 
					      <li v-if="status !== Statuses.ACCEPTED">
 | 
				
			||||||
 | 
					        <a class="dropdown-item" @click="changeStatus(Statuses.ACCEPTED)"
 | 
				
			||||||
 | 
					          ><i class="fa fa-check" aria-hidden="true"></i> {{ $t("Accept") }}</a
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
            <template v-if="status === Statuses.PENDING">
 | 
					      </li>
 | 
				
			||||||
                <span class="fa fa-hourglass"></span> {{ $t("Give_an_answer") }}
 | 
					      <li v-if="status !== Statuses.DECLINED">
 | 
				
			||||||
            </template>
 | 
					        <a class="dropdown-item" @click="changeStatus(Statuses.DECLINED)"
 | 
				
			||||||
            <template v-else-if="status === Statuses.ACCEPTED">
 | 
					          ><i class="fa fa-times" aria-hidden="true"></i> {{ $t("Decline") }}</a
 | 
				
			||||||
                <span class="fa fa-check"></span> {{ $t("Accepted") }}
 | 
					        >
 | 
				
			||||||
            </template>
 | 
					      </li>
 | 
				
			||||||
            <template v-else-if="status === Statuses.DECLINED">
 | 
					      <li v-if="status !== Statuses.TENTATIVELY_ACCEPTED">
 | 
				
			||||||
                <span class="fa fa-times"></span> {{ $t("Declined") }}
 | 
					        <a
 | 
				
			||||||
            </template>
 | 
					          class="dropdown-item"
 | 
				
			||||||
            <template v-else-if="status === Statuses.TENTATIVELY_ACCEPTED">
 | 
					          @click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"
 | 
				
			||||||
                <span class="fa fa-question"></span> {{ $t("Tentative") }}
 | 
					          ><i class="fa fa-question"></i> {{ $t("Tentatively_accept") }}</a
 | 
				
			||||||
            </template>
 | 
					        >
 | 
				
			||||||
        </button>
 | 
					      </li>
 | 
				
			||||||
        <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
 | 
					      <li v-if="status !== Statuses.PENDING">
 | 
				
			||||||
            <li v-if="status !== Statuses.ACCEPTED">
 | 
					        <a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"
 | 
				
			||||||
                <a
 | 
					          ><i class="fa fa-hourglass-o"></i> {{ $t("Set_pending") }}</a
 | 
				
			||||||
                    class="dropdown-item"
 | 
					        >
 | 
				
			||||||
                    @click="changeStatus(Statuses.ACCEPTED)"
 | 
					      </li>
 | 
				
			||||||
                    ><i class="fa fa-check" aria-hidden="true"></i>
 | 
					    </ul>
 | 
				
			||||||
                    {{ $t("Accept") }}</a
 | 
					  </div>
 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
            <li v-if="status !== Statuses.DECLINED">
 | 
					 | 
				
			||||||
                <a
 | 
					 | 
				
			||||||
                    class="dropdown-item"
 | 
					 | 
				
			||||||
                    @click="changeStatus(Statuses.DECLINED)"
 | 
					 | 
				
			||||||
                    ><i class="fa fa-times" aria-hidden="true"></i>
 | 
					 | 
				
			||||||
                    {{ $t("Decline") }}</a
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
            <li v-if="status !== Statuses.TENTATIVELY_ACCEPTED">
 | 
					 | 
				
			||||||
                <a
 | 
					 | 
				
			||||||
                    class="dropdown-item"
 | 
					 | 
				
			||||||
                    @click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"
 | 
					 | 
				
			||||||
                    ><i class="fa fa-question"></i>
 | 
					 | 
				
			||||||
                    {{ $t("Tentatively_accept") }}</a
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
            <li v-if="status !== Statuses.PENDING">
 | 
					 | 
				
			||||||
                <a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"
 | 
					 | 
				
			||||||
                    ><i class="fa fa-hourglass-o"></i>
 | 
					 | 
				
			||||||
                    {{ $t("Set_pending") }}</a
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
        </ul>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
@@ -64,69 +56,67 @@ const PENDING = "pending";
 | 
				
			|||||||
const TENTATIVELY_ACCEPTED = "tentative";
 | 
					const TENTATIVELY_ACCEPTED = "tentative";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const i18n = {
 | 
					const i18n = {
 | 
				
			||||||
    messages: {
 | 
					  messages: {
 | 
				
			||||||
        fr: {
 | 
					    fr: {
 | 
				
			||||||
            Give_an_answer: "Répondre",
 | 
					      Give_an_answer: "Répondre",
 | 
				
			||||||
            Accepted: "Accepté",
 | 
					      Accepted: "Accepté",
 | 
				
			||||||
            Declined: "Refusé",
 | 
					      Declined: "Refusé",
 | 
				
			||||||
            Tentative: "Accepté provisoirement",
 | 
					      Tentative: "Accepté provisoirement",
 | 
				
			||||||
            Accept: "Accepter",
 | 
					      Accept: "Accepter",
 | 
				
			||||||
            Decline: "Refuser",
 | 
					      Decline: "Refuser",
 | 
				
			||||||
            Tentatively_accept: "Accepter provisoirement",
 | 
					      Tentatively_accept: "Accepter provisoirement",
 | 
				
			||||||
            Set_pending: "Ne pas répondre",
 | 
					      Set_pending: "Ne pas répondre",
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default defineComponent({
 | 
					export default defineComponent({
 | 
				
			||||||
    name: "Answer",
 | 
					  name: "Answer",
 | 
				
			||||||
    i18n,
 | 
					  i18n,
 | 
				
			||||||
    props: {
 | 
					  props: {
 | 
				
			||||||
        calendarId: { type: Number, required: true },
 | 
					    calendarId: { type: Number, required: true },
 | 
				
			||||||
        status: {
 | 
					    status: {
 | 
				
			||||||
            type: String as PropType<
 | 
					      type: String as PropType<
 | 
				
			||||||
                "accepted" | "declined" | "pending" | "tentative"
 | 
					        "accepted" | "declined" | "pending" | "tentative"
 | 
				
			||||||
            >,
 | 
					      >,
 | 
				
			||||||
            required: true,
 | 
					      required: true,
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    emits: {
 | 
					  },
 | 
				
			||||||
        statusChanged(
 | 
					  emits: {
 | 
				
			||||||
            payload: "accepted" | "declined" | "pending" | "tentative",
 | 
					    statusChanged(payload: "accepted" | "declined" | "pending" | "tentative") {
 | 
				
			||||||
        ) {
 | 
					      return true;
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    data() {
 | 
					  },
 | 
				
			||||||
        return {
 | 
					  data() {
 | 
				
			||||||
            Statuses: {
 | 
					    return {
 | 
				
			||||||
                ACCEPTED,
 | 
					      Statuses: {
 | 
				
			||||||
                DECLINED,
 | 
					        ACCEPTED,
 | 
				
			||||||
                PENDING,
 | 
					        DECLINED,
 | 
				
			||||||
                TENTATIVELY_ACCEPTED,
 | 
					        PENDING,
 | 
				
			||||||
            },
 | 
					        TENTATIVELY_ACCEPTED,
 | 
				
			||||||
        };
 | 
					      },
 | 
				
			||||||
    },
 | 
					    };
 | 
				
			||||||
    methods: {
 | 
					  },
 | 
				
			||||||
        changeStatus: function (
 | 
					  methods: {
 | 
				
			||||||
            newStatus: "accepted" | "declined" | "pending" | "tentative",
 | 
					    changeStatus: function (
 | 
				
			||||||
        ) {
 | 
					      newStatus: "accepted" | "declined" | "pending" | "tentative",
 | 
				
			||||||
            console.log("changeStatus", newStatus);
 | 
					    ) {
 | 
				
			||||||
            const url = `/api/1.0/calendar/calendar/${this.$props.calendarId}/answer/${newStatus}.json`;
 | 
					      console.log("changeStatus", newStatus);
 | 
				
			||||||
            window
 | 
					      const url = `/api/1.0/calendar/calendar/${this.$props.calendarId}/answer/${newStatus}.json`;
 | 
				
			||||||
                .fetch(url, {
 | 
					      window
 | 
				
			||||||
                    method: "POST",
 | 
					        .fetch(url, {
 | 
				
			||||||
                })
 | 
					          method: "POST",
 | 
				
			||||||
                .then((r: Response) => {
 | 
					        })
 | 
				
			||||||
                    if (!r.ok) {
 | 
					        .then((r: Response) => {
 | 
				
			||||||
                        console.error("could not confirm answer", newStatus);
 | 
					          if (!r.ok) {
 | 
				
			||||||
                        return;
 | 
					            console.error("could not confirm answer", newStatus);
 | 
				
			||||||
                    }
 | 
					            return;
 | 
				
			||||||
                    console.log("answer sent", newStatus);
 | 
					          }
 | 
				
			||||||
                    this.$emit("statusChanged", newStatus);
 | 
					          console.log("answer sent", newStatus);
 | 
				
			||||||
                });
 | 
					          this.$emit("statusChanged", newStatus);
 | 
				
			||||||
        },
 | 
					        });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,225 +1,177 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="row">
 | 
					  <div class="row">
 | 
				
			||||||
        <div class="col-sm">
 | 
					    <div class="col-sm">
 | 
				
			||||||
            <label class="form-label">{{ $t("created_availabilities") }}</label>
 | 
					      <label class="form-label">{{ $t("created_availabilities") }}</label>
 | 
				
			||||||
            <vue-multiselect
 | 
					      <vue-multiselect
 | 
				
			||||||
                v-model="pickedLocation"
 | 
					        v-model="pickedLocation"
 | 
				
			||||||
                :options="locations"
 | 
					        :options="locations"
 | 
				
			||||||
                :label="'name'"
 | 
					        :label="'name'"
 | 
				
			||||||
                :track-by="'id'"
 | 
					        :track-by="'id'"
 | 
				
			||||||
                :selectLabel="'Presser \'Entrée\' pour choisir'"
 | 
					        :selectLabel="'Presser \'Entrée\' pour choisir'"
 | 
				
			||||||
                :selectedLabel="'Choisir'"
 | 
					        :selectedLabel="'Choisir'"
 | 
				
			||||||
                :deselectLabel="'Presser \'Entrée\' pour enlever'"
 | 
					        :deselectLabel="'Presser \'Entrée\' pour enlever'"
 | 
				
			||||||
                :placeholder="'Choisir'"
 | 
					        :placeholder="'Choisir'"
 | 
				
			||||||
            ></vue-multiselect>
 | 
					      ></vue-multiselect>
 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div
 | 
					  </div>
 | 
				
			||||||
        class="display-options row justify-content-between"
 | 
					  <div
 | 
				
			||||||
        style="margin-top: 1rem"
 | 
					    class="display-options row justify-content-between"
 | 
				
			||||||
    >
 | 
					    style="margin-top: 1rem"
 | 
				
			||||||
        <div class="col-sm-9 col-xs-12">
 | 
					  >
 | 
				
			||||||
            <div class="input-group mb-3">
 | 
					    <div class="col-sm-9 col-xs-12">
 | 
				
			||||||
                <label class="input-group-text" for="slotDuration"
 | 
					      <div class="input-group mb-3">
 | 
				
			||||||
                    >Durée des créneaux</label
 | 
					        <label class="input-group-text" for="slotDuration"
 | 
				
			||||||
                >
 | 
					          >Durée des créneaux</label
 | 
				
			||||||
                <select
 | 
					        >
 | 
				
			||||||
                    v-model="slotDuration"
 | 
					        <select v-model="slotDuration" id="slotDuration" class="form-select">
 | 
				
			||||||
                    id="slotDuration"
 | 
					          <option value="00:05:00">5 minutes</option>
 | 
				
			||||||
                    class="form-select"
 | 
					          <option value="00:10:00">10 minutes</option>
 | 
				
			||||||
                >
 | 
					          <option value="00:15:00">15 minutes</option>
 | 
				
			||||||
                    <option value="00:05:00">5 minutes</option>
 | 
					          <option value="00:30:00">30 minutes</option>
 | 
				
			||||||
                    <option value="00:10:00">10 minutes</option>
 | 
					        </select>
 | 
				
			||||||
                    <option value="00:15:00">15 minutes</option>
 | 
					        <label class="input-group-text" for="slotMinTime">De</label>
 | 
				
			||||||
                    <option value="00:30:00">30 minutes</option>
 | 
					        <select v-model="slotMinTime" id="slotMinTime" class="form-select">
 | 
				
			||||||
                </select>
 | 
					          <option value="00:00:00">0h</option>
 | 
				
			||||||
                <label class="input-group-text" for="slotMinTime">De</label>
 | 
					          <option value="01:00:00">1h</option>
 | 
				
			||||||
                <select
 | 
					          <option value="02:00:00">2h</option>
 | 
				
			||||||
                    v-model="slotMinTime"
 | 
					          <option value="03:00:00">3h</option>
 | 
				
			||||||
                    id="slotMinTime"
 | 
					          <option value="04:00:00">4h</option>
 | 
				
			||||||
                    class="form-select"
 | 
					          <option value="05:00:00">5h</option>
 | 
				
			||||||
                >
 | 
					          <option value="06:00:00">6h</option>
 | 
				
			||||||
                    <option value="00:00:00">0h</option>
 | 
					          <option value="07:00:00">7h</option>
 | 
				
			||||||
                    <option value="01:00:00">1h</option>
 | 
					          <option value="08:00:00">8h</option>
 | 
				
			||||||
                    <option value="02:00:00">2h</option>
 | 
					          <option value="09:00:00">9h</option>
 | 
				
			||||||
                    <option value="03:00:00">3h</option>
 | 
					          <option value="10:00:00">10h</option>
 | 
				
			||||||
                    <option value="04:00:00">4h</option>
 | 
					          <option value="11:00:00">11h</option>
 | 
				
			||||||
                    <option value="05:00:00">5h</option>
 | 
					          <option value="12:00:00">12h</option>
 | 
				
			||||||
                    <option value="06:00:00">6h</option>
 | 
					        </select>
 | 
				
			||||||
                    <option value="07:00:00">7h</option>
 | 
					        <label class="input-group-text" for="slotMaxTime">À</label>
 | 
				
			||||||
                    <option value="08:00:00">8h</option>
 | 
					        <select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
 | 
				
			||||||
                    <option value="09:00:00">9h</option>
 | 
					          <option value="12:00:00">12h</option>
 | 
				
			||||||
                    <option value="10:00:00">10h</option>
 | 
					          <option value="13:00:00">13h</option>
 | 
				
			||||||
                    <option value="11:00:00">11h</option>
 | 
					          <option value="14:00:00">14h</option>
 | 
				
			||||||
                    <option value="12:00:00">12h</option>
 | 
					          <option value="15:00:00">15h</option>
 | 
				
			||||||
                </select>
 | 
					          <option value="16:00:00">16h</option>
 | 
				
			||||||
                <label class="input-group-text" for="slotMaxTime">À</label>
 | 
					          <option value="17:00:00">17h</option>
 | 
				
			||||||
                <select
 | 
					          <option value="18:00:00">18h</option>
 | 
				
			||||||
                    v-model="slotMaxTime"
 | 
					          <option value="19:00:00">19h</option>
 | 
				
			||||||
                    id="slotMaxTime"
 | 
					          <option value="20:00:00">20h</option>
 | 
				
			||||||
                    class="form-select"
 | 
					          <option value="21:00:00">21h</option>
 | 
				
			||||||
                >
 | 
					          <option value="22:00:00">22h</option>
 | 
				
			||||||
                    <option value="12:00:00">12h</option>
 | 
					          <option value="23:00:00">23h</option>
 | 
				
			||||||
                    <option value="13:00:00">13h</option>
 | 
					          <option value="23:59:59">24h</option>
 | 
				
			||||||
                    <option value="14:00:00">14h</option>
 | 
					        </select>
 | 
				
			||||||
                    <option value="15:00:00">15h</option>
 | 
					      </div>
 | 
				
			||||||
                    <option value="16:00:00">16h</option>
 | 
					 | 
				
			||||||
                    <option value="17:00:00">17h</option>
 | 
					 | 
				
			||||||
                    <option value="18:00:00">18h</option>
 | 
					 | 
				
			||||||
                    <option value="19:00:00">19h</option>
 | 
					 | 
				
			||||||
                    <option value="20:00:00">20h</option>
 | 
					 | 
				
			||||||
                    <option value="21:00:00">21h</option>
 | 
					 | 
				
			||||||
                    <option value="22:00:00">22h</option>
 | 
					 | 
				
			||||||
                    <option value="23:00:00">23h</option>
 | 
					 | 
				
			||||||
                    <option value="23:59:59">24h</option>
 | 
					 | 
				
			||||||
                </select>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="col-xs-12 col-sm-3">
 | 
					 | 
				
			||||||
            <div class="float-end">
 | 
					 | 
				
			||||||
                <div class="form-check input-group">
 | 
					 | 
				
			||||||
                    <span class="input-group-text">
 | 
					 | 
				
			||||||
                        <input
 | 
					 | 
				
			||||||
                            id="showHideWE"
 | 
					 | 
				
			||||||
                            class="mt-0"
 | 
					 | 
				
			||||||
                            type="checkbox"
 | 
					 | 
				
			||||||
                            v-model="showWeekends"
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    </span>
 | 
					 | 
				
			||||||
                    <label
 | 
					 | 
				
			||||||
                        for="showHideWE"
 | 
					 | 
				
			||||||
                        class="form-check-label input-group-text"
 | 
					 | 
				
			||||||
                        >Week-ends</label
 | 
					 | 
				
			||||||
                    >
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <FullCalendar :options="calendarOptions" ref="calendarRef">
 | 
					    <div class="col-xs-12 col-sm-3">
 | 
				
			||||||
        <template v-slot:eventContent="{ event }: { event: EventApi }">
 | 
					      <div class="float-end">
 | 
				
			||||||
            <span :class="eventClasses">
 | 
					        <div class="form-check input-group">
 | 
				
			||||||
                <b v-if="event.extendedProps.is === 'remote'">{{
 | 
					          <span class="input-group-text">
 | 
				
			||||||
                    event.title
 | 
					            <input
 | 
				
			||||||
                }}</b>
 | 
					              id="showHideWE"
 | 
				
			||||||
                <b v-else-if="event.extendedProps.is === 'range'"
 | 
					              class="mt-0"
 | 
				
			||||||
                    >{{ formatDate(event.startStr) }} -
 | 
					              type="checkbox"
 | 
				
			||||||
                    {{ event.extendedProps.locationName }}</b
 | 
					              v-model="showWeekends"
 | 
				
			||||||
                >
 | 
					            />
 | 
				
			||||||
                <b v-else-if="event.extendedProps.is === 'local'">{{
 | 
					          </span>
 | 
				
			||||||
                    event.title
 | 
					          <label for="showHideWE" class="form-check-label input-group-text"
 | 
				
			||||||
                }}</b>
 | 
					            >Week-ends</label
 | 
				
			||||||
                <b v-else>no 'is'</b>
 | 
					          >
 | 
				
			||||||
                <a
 | 
					        </div>
 | 
				
			||||||
                    v-if="event.extendedProps.is === 'range'"
 | 
					      </div>
 | 
				
			||||||
                    class="fa fa-fw fa-times delete"
 | 
					    </div>
 | 
				
			||||||
                    @click.prevent="onClickDelete(event)"
 | 
					  </div>
 | 
				
			||||||
                >
 | 
					  <FullCalendar :options="calendarOptions" ref="calendarRef">
 | 
				
			||||||
                </a>
 | 
					    <template v-slot:eventContent="{ event }: { event: EventApi }">
 | 
				
			||||||
            </span>
 | 
					      <span :class="eventClasses">
 | 
				
			||||||
 | 
					        <b v-if="event.extendedProps.is === 'remote'">{{ event.title }}</b>
 | 
				
			||||||
 | 
					        <b v-else-if="event.extendedProps.is === 'range'"
 | 
				
			||||||
 | 
					          >{{ formatDate(event.startStr) }} -
 | 
				
			||||||
 | 
					          {{ event.extendedProps.locationName }}</b
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					        <b v-else-if="event.extendedProps.is === 'local'">{{ event.title }}</b>
 | 
				
			||||||
 | 
					        <b v-else>no 'is'</b>
 | 
				
			||||||
 | 
					        <a
 | 
				
			||||||
 | 
					          v-if="event.extendedProps.is === 'range'"
 | 
				
			||||||
 | 
					          class="fa fa-fw fa-times delete"
 | 
				
			||||||
 | 
					          @click.prevent="onClickDelete(event)"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					  </FullCalendar>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div id="copy-widget">
 | 
				
			||||||
 | 
					    <div class="container mt-2 mb-2">
 | 
				
			||||||
 | 
					      <div class="row justify-content-between align-items-center mb-4">
 | 
				
			||||||
 | 
					        <div class="col-xs-12 col-sm-3 col-md-2">
 | 
				
			||||||
 | 
					          <h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="col-xs-12 col-sm-9 col-md-2">
 | 
				
			||||||
 | 
					          <select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
 | 
				
			||||||
 | 
					            <option value="day">{{ $t("from_day_to_day") }}</option>
 | 
				
			||||||
 | 
					            <option value="week">
 | 
				
			||||||
 | 
					              {{ $t("from_week_to_week") }}
 | 
				
			||||||
 | 
					            </option>
 | 
				
			||||||
 | 
					          </select>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <template v-if="dayOrWeek === 'day'">
 | 
				
			||||||
 | 
					          <div class="col-xs-12 col-sm-3 col-md-3">
 | 
				
			||||||
 | 
					            <input class="form-control" type="date" v-model="copyFrom" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
 | 
				
			||||||
 | 
					            <i class="fa fa-angle-double-right"></i>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="col-xs-12 col-sm-3 col-md-3">
 | 
				
			||||||
 | 
					            <input class="form-control" type="date" v-model="copyTo" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="col-xs-12 col-sm-5 col-md-1">
 | 
				
			||||||
 | 
					            <button class="btn btn-action float-end" @click="copyDay">
 | 
				
			||||||
 | 
					              {{ $t("copy_range") }}
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
        </template>
 | 
					        </template>
 | 
				
			||||||
    </FullCalendar>
 | 
					        <template v-else>
 | 
				
			||||||
 | 
					          <div class="col-xs-12 col-sm-3 col-md-3">
 | 
				
			||||||
    <div id="copy-widget">
 | 
					            <select
 | 
				
			||||||
        <div class="container mt-2 mb-2">
 | 
					              v-model="copyFromWeek"
 | 
				
			||||||
            <div class="row justify-content-between align-items-center mb-4">
 | 
					              id="copyFromWeek"
 | 
				
			||||||
                <div class="col-xs-12 col-sm-3 col-md-2">
 | 
					              class="form-select"
 | 
				
			||||||
                    <h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
 | 
					            >
 | 
				
			||||||
                </div>
 | 
					              <option v-for="w in lastWeeks" :value="w.value" :key="w.value">
 | 
				
			||||||
                <div class="col-xs-12 col-sm-9 col-md-2">
 | 
					                {{ w.text }}
 | 
				
			||||||
                    <select
 | 
					              </option>
 | 
				
			||||||
                        v-model="dayOrWeek"
 | 
					            </select>
 | 
				
			||||||
                        id="dayOrWeek"
 | 
					          </div>
 | 
				
			||||||
                        class="form-select"
 | 
					          <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
 | 
				
			||||||
                    >
 | 
					            <i class="fa fa-angle-double-right"></i>
 | 
				
			||||||
                        <option value="day">{{ $t("from_day_to_day") }}</option>
 | 
					          </div>
 | 
				
			||||||
                        <option value="week">
 | 
					          <div class="col-xs-12 col-sm-3 col-md-3">
 | 
				
			||||||
                            {{ $t("from_week_to_week") }}
 | 
					            <select v-model="copyToWeek" id="copyToWeek" class="form-select">
 | 
				
			||||||
                        </option>
 | 
					              <option v-for="w in nextWeeks" :value="w.value" :key="w.value">
 | 
				
			||||||
                    </select>
 | 
					                {{ w.text }}
 | 
				
			||||||
                </div>
 | 
					              </option>
 | 
				
			||||||
                <template v-if="dayOrWeek === 'day'">
 | 
					            </select>
 | 
				
			||||||
                    <div class="col-xs-12 col-sm-3 col-md-3">
 | 
					          </div>
 | 
				
			||||||
                        <input
 | 
					          <div class="col-xs-12 col-sm-5 col-md-1">
 | 
				
			||||||
                            class="form-control"
 | 
					            <button class="btn btn-action float-end" @click="copyWeek">
 | 
				
			||||||
                            type="date"
 | 
					              {{ $t("copy_range") }}
 | 
				
			||||||
                            v-model="copyFrom"
 | 
					            </button>
 | 
				
			||||||
                        />
 | 
					          </div>
 | 
				
			||||||
                    </div>
 | 
					        </template>
 | 
				
			||||||
                    <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
 | 
					      </div>
 | 
				
			||||||
                        <i class="fa fa-angle-double-right"></i>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-3 col-md-3">
 | 
					 | 
				
			||||||
                        <input
 | 
					 | 
				
			||||||
                            class="form-control"
 | 
					 | 
				
			||||||
                            type="date"
 | 
					 | 
				
			||||||
                            v-model="copyTo"
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-5 col-md-1">
 | 
					 | 
				
			||||||
                        <button
 | 
					 | 
				
			||||||
                            class="btn btn-action float-end"
 | 
					 | 
				
			||||||
                            @click="copyDay"
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            {{ $t("copy_range") }}
 | 
					 | 
				
			||||||
                        </button>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </template>
 | 
					 | 
				
			||||||
                <template v-else>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-3 col-md-3">
 | 
					 | 
				
			||||||
                        <select
 | 
					 | 
				
			||||||
                            v-model="copyFromWeek"
 | 
					 | 
				
			||||||
                            id="copyFromWeek"
 | 
					 | 
				
			||||||
                            class="form-select"
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            <option
 | 
					 | 
				
			||||||
                                v-for="w in lastWeeks"
 | 
					 | 
				
			||||||
                                :value="w.value"
 | 
					 | 
				
			||||||
                                :key="w.value"
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                {{ w.text }}
 | 
					 | 
				
			||||||
                            </option>
 | 
					 | 
				
			||||||
                        </select>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
 | 
					 | 
				
			||||||
                        <i class="fa fa-angle-double-right"></i>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-3 col-md-3">
 | 
					 | 
				
			||||||
                        <select
 | 
					 | 
				
			||||||
                            v-model="copyToWeek"
 | 
					 | 
				
			||||||
                            id="copyToWeek"
 | 
					 | 
				
			||||||
                            class="form-select"
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            <option
 | 
					 | 
				
			||||||
                                v-for="w in nextWeeks"
 | 
					 | 
				
			||||||
                                :value="w.value"
 | 
					 | 
				
			||||||
                                :key="w.value"
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                {{ w.text }}
 | 
					 | 
				
			||||||
                            </option>
 | 
					 | 
				
			||||||
                        </select>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="col-xs-12 col-sm-5 col-md-1">
 | 
					 | 
				
			||||||
                        <button
 | 
					 | 
				
			||||||
                            class="btn btn-action float-end"
 | 
					 | 
				
			||||||
                            @click="copyWeek"
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            {{ $t("copy_range") }}
 | 
					 | 
				
			||||||
                        </button>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </template>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- not directly seen, but include in a modal -->
 | 
					  <!-- not directly seen, but include in a modal -->
 | 
				
			||||||
    <edit-location ref="editLocation"></edit-location>
 | 
					  <edit-location ref="editLocation"></edit-location>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
    CalendarOptions,
 | 
					  CalendarOptions,
 | 
				
			||||||
    DatesSetArg,
 | 
					  DatesSetArg,
 | 
				
			||||||
    EventInput,
 | 
					  EventInput,
 | 
				
			||||||
} from "@fullcalendar/core";
 | 
					} from "@fullcalendar/core";
 | 
				
			||||||
import { computed, ref, onMounted } from "vue";
 | 
					import { computed, ref, onMounted } from "vue";
 | 
				
			||||||
import { useStore } from "vuex";
 | 
					import { useStore } from "vuex";
 | 
				
			||||||
@@ -227,14 +179,14 @@ import { key } from "./store";
 | 
				
			|||||||
import FullCalendar from "@fullcalendar/vue3";
 | 
					import FullCalendar from "@fullcalendar/vue3";
 | 
				
			||||||
import frLocale from "@fullcalendar/core/locales/fr";
 | 
					import frLocale from "@fullcalendar/core/locales/fr";
 | 
				
			||||||
import interactionPlugin, {
 | 
					import interactionPlugin, {
 | 
				
			||||||
    EventResizeDoneArg,
 | 
					  EventResizeDoneArg,
 | 
				
			||||||
} from "@fullcalendar/interaction";
 | 
					} from "@fullcalendar/interaction";
 | 
				
			||||||
import timeGridPlugin from "@fullcalendar/timegrid";
 | 
					import timeGridPlugin from "@fullcalendar/timegrid";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    EventApi,
 | 
					  EventApi,
 | 
				
			||||||
    DateSelectArg,
 | 
					  DateSelectArg,
 | 
				
			||||||
    EventDropArg,
 | 
					  EventDropArg,
 | 
				
			||||||
    EventClickArg,
 | 
					  EventClickArg,
 | 
				
			||||||
} from "@fullcalendar/core";
 | 
					} from "@fullcalendar/core";
 | 
				
			||||||
import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date";
 | 
					import { dateToISO, ISOToDate } from "ChillMainAssets/chill/js/date";
 | 
				
			||||||
import VueMultiselect from "vue-multiselect";
 | 
					import VueMultiselect from "vue-multiselect";
 | 
				
			||||||
@@ -255,96 +207,96 @@ const copyFromWeek = ref<string | null>(null);
 | 
				
			|||||||
const copyToWeek = ref<string | null>(null);
 | 
					const copyToWeek = ref<string | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Weeks {
 | 
					interface Weeks {
 | 
				
			||||||
    value: string | null;
 | 
					  value: string | null;
 | 
				
			||||||
    text: string;
 | 
					  text: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getMonday = (week: number): Date => {
 | 
					const getMonday = (week: number): Date => {
 | 
				
			||||||
    const lastMonday = new Date();
 | 
					  const lastMonday = new Date();
 | 
				
			||||||
    lastMonday.setDate(
 | 
					  lastMonday.setDate(
 | 
				
			||||||
        lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7,
 | 
					    lastMonday.getDate() - ((lastMonday.getDay() + 6) % 7) + week * 7,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
    return lastMonday;
 | 
					  return lastMonday;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dateOptions: Intl.DateTimeFormatOptions = {
 | 
					const dateOptions: Intl.DateTimeFormatOptions = {
 | 
				
			||||||
    weekday: "long",
 | 
					  weekday: "long",
 | 
				
			||||||
    year: "numeric",
 | 
					  year: "numeric",
 | 
				
			||||||
    month: "long",
 | 
					  month: "long",
 | 
				
			||||||
    day: "numeric",
 | 
					  day: "numeric",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const lastWeeks = computed((): Weeks[] =>
 | 
					const lastWeeks = computed((): Weeks[] =>
 | 
				
			||||||
    Array.from(Array(30).keys()).map((w) => {
 | 
					  Array.from(Array(30).keys()).map((w) => {
 | 
				
			||||||
        const lastMonday = getMonday(15 - w);
 | 
					    const lastMonday = getMonday(15 - w);
 | 
				
			||||||
        return {
 | 
					    return {
 | 
				
			||||||
            value: dateToISO(lastMonday),
 | 
					      value: dateToISO(lastMonday),
 | 
				
			||||||
            text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
 | 
					      text: `Semaine du ${lastMonday.toLocaleDateString("fr-FR", dateOptions)}`,
 | 
				
			||||||
        };
 | 
					    };
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const nextWeeks = computed((): Weeks[] =>
 | 
					const nextWeeks = computed((): Weeks[] =>
 | 
				
			||||||
    Array.from(Array(52).keys()).map((w) => {
 | 
					  Array.from(Array(52).keys()).map((w) => {
 | 
				
			||||||
        const nextMonday = getMonday(w + 1);
 | 
					    const nextMonday = getMonday(w + 1);
 | 
				
			||||||
        return {
 | 
					    return {
 | 
				
			||||||
            value: dateToISO(nextMonday),
 | 
					      value: dateToISO(nextMonday),
 | 
				
			||||||
            text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
 | 
					      text: `Semaine du ${nextMonday.toLocaleDateString("fr-FR", dateOptions)}`,
 | 
				
			||||||
        };
 | 
					    };
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formatDate = (datetime: string) => {
 | 
					const formatDate = (datetime: string) => {
 | 
				
			||||||
    console.log(typeof datetime);
 | 
					  console.log(typeof datetime);
 | 
				
			||||||
    return ISOToDate(datetime);
 | 
					  return ISOToDate(datetime);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const baseOptions = ref<CalendarOptions>({
 | 
					const baseOptions = ref<CalendarOptions>({
 | 
				
			||||||
    locale: frLocale,
 | 
					  locale: frLocale,
 | 
				
			||||||
    plugins: [interactionPlugin, timeGridPlugin],
 | 
					  plugins: [interactionPlugin, timeGridPlugin],
 | 
				
			||||||
    initialView: "timeGridWeek",
 | 
					  initialView: "timeGridWeek",
 | 
				
			||||||
    initialDate: new Date(),
 | 
					  initialDate: new Date(),
 | 
				
			||||||
    scrollTimeReset: false,
 | 
					  scrollTimeReset: false,
 | 
				
			||||||
    selectable: true,
 | 
					  selectable: true,
 | 
				
			||||||
    // when the dates are changes in the fullcalendar view OR when new events are added
 | 
					  // when the dates are changes in the fullcalendar view OR when new events are added
 | 
				
			||||||
    datesSet: onDatesSet,
 | 
					  datesSet: onDatesSet,
 | 
				
			||||||
    // when a date is selected
 | 
					  // when a date is selected
 | 
				
			||||||
    select: onDateSelect,
 | 
					  select: onDateSelect,
 | 
				
			||||||
    // when a event is resized
 | 
					  // when a event is resized
 | 
				
			||||||
    eventResize: onEventDropOrResize,
 | 
					  eventResize: onEventDropOrResize,
 | 
				
			||||||
    // when an event is moved
 | 
					  // when an event is moved
 | 
				
			||||||
    eventDrop: onEventDropOrResize,
 | 
					  eventDrop: onEventDropOrResize,
 | 
				
			||||||
    // when an event si clicked
 | 
					  // when an event si clicked
 | 
				
			||||||
    eventClick: onEventClick,
 | 
					  eventClick: onEventClick,
 | 
				
			||||||
    selectMirror: false,
 | 
					  selectMirror: false,
 | 
				
			||||||
    editable: true,
 | 
					  editable: true,
 | 
				
			||||||
    headerToolbar: {
 | 
					  headerToolbar: {
 | 
				
			||||||
        left: "prev,next today",
 | 
					    left: "prev,next today",
 | 
				
			||||||
        center: "title",
 | 
					    center: "title",
 | 
				
			||||||
        right: "timeGridWeek,timeGridDay",
 | 
					    right: "timeGridWeek,timeGridDay",
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ranges = computed<EventInput[]>(() => {
 | 
					const ranges = computed<EventInput[]>(() => {
 | 
				
			||||||
    return store.state.calendarRanges.ranges;
 | 
					  return store.state.calendarRanges.ranges;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const locations = computed<Location[]>(() => {
 | 
					const locations = computed<Location[]>(() => {
 | 
				
			||||||
    return store.state.locations.locations;
 | 
					  return store.state.locations.locations;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const pickedLocation = computed<Location | null>({
 | 
					const pickedLocation = computed<Location | null>({
 | 
				
			||||||
    get(): Location | null {
 | 
					  get(): Location | null {
 | 
				
			||||||
        return (
 | 
					    return (
 | 
				
			||||||
            store.state.locations.locationPicked ||
 | 
					      store.state.locations.locationPicked ||
 | 
				
			||||||
            store.state.locations.currentLocation
 | 
					      store.state.locations.currentLocation
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
    set(newLocation: Location | null): void {
 | 
					  set(newLocation: Location | null): void {
 | 
				
			||||||
        store.commit("locations/setLocationPicked", newLocation, {
 | 
					    store.commit("locations/setLocationPicked", newLocation, {
 | 
				
			||||||
            root: true,
 | 
					      root: true,
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -373,116 +325,116 @@ const sources = computed<EventSourceInput[]>(() => {
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const calendarOptions = computed((): CalendarOptions => {
 | 
					const calendarOptions = computed((): CalendarOptions => {
 | 
				
			||||||
    return {
 | 
					  return {
 | 
				
			||||||
        ...baseOptions.value,
 | 
					    ...baseOptions.value,
 | 
				
			||||||
        weekends: showWeekends.value,
 | 
					    weekends: showWeekends.value,
 | 
				
			||||||
        slotDuration: slotDuration.value,
 | 
					    slotDuration: slotDuration.value,
 | 
				
			||||||
        events: ranges.value,
 | 
					    events: ranges.value,
 | 
				
			||||||
        slotMinTime: slotMinTime.value,
 | 
					    slotMinTime: slotMinTime.value,
 | 
				
			||||||
        slotMaxTime: slotMaxTime.value,
 | 
					    slotMaxTime: slotMaxTime.value,
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * launched when the calendar range date change
 | 
					 * launched when the calendar range date change
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function onDatesSet(event: DatesSetArg): void {
 | 
					function onDatesSet(event: DatesSetArg): void {
 | 
				
			||||||
    store.dispatch("fullCalendar/setCurrentDatesView", {
 | 
					  store.dispatch("fullCalendar/setCurrentDatesView", {
 | 
				
			||||||
        start: event.start,
 | 
					    start: event.start,
 | 
				
			||||||
        end: event.end,
 | 
					    end: event.end,
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onDateSelect(event: DateSelectArg): void {
 | 
					function onDateSelect(event: DateSelectArg): void {
 | 
				
			||||||
    if (null === pickedLocation.value) {
 | 
					  if (null === pickedLocation.value) {
 | 
				
			||||||
        window.alert(
 | 
					    window.alert(
 | 
				
			||||||
            "Indiquez une localisation avant de créer une période de disponibilité.",
 | 
					      "Indiquez une localisation avant de créer une période de disponibilité.",
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store.dispatch("calendarRanges/createRange", {
 | 
					  store.dispatch("calendarRanges/createRange", {
 | 
				
			||||||
        start: event.start,
 | 
					    start: event.start,
 | 
				
			||||||
        end: event.end,
 | 
					    end: event.end,
 | 
				
			||||||
        location: pickedLocation.value,
 | 
					    location: pickedLocation.value,
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * When a calendar range is deleted
 | 
					 * When a calendar range is deleted
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function onClickDelete(event: EventApi): void {
 | 
					function onClickDelete(event: EventApi): void {
 | 
				
			||||||
    if (event.extendedProps.is !== "range") {
 | 
					  if (event.extendedProps.is !== "range") {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store.dispatch(
 | 
					  store.dispatch(
 | 
				
			||||||
        "calendarRanges/deleteRange",
 | 
					    "calendarRanges/deleteRange",
 | 
				
			||||||
        event.extendedProps.calendarRangeId,
 | 
					    event.extendedProps.calendarRangeId,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
 | 
					function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
 | 
				
			||||||
    if (payload.event.extendedProps.is !== "range") {
 | 
					  if (payload.event.extendedProps.is !== "range") {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store.dispatch("calendarRanges/patchRangeTime", {
 | 
					  store.dispatch("calendarRanges/patchRangeTime", {
 | 
				
			||||||
        calendarRangeId: payload.event.extendedProps.calendarRangeId,
 | 
					    calendarRangeId: payload.event.extendedProps.calendarRangeId,
 | 
				
			||||||
        start: payload.event.start,
 | 
					    start: payload.event.start,
 | 
				
			||||||
        end: payload.event.end,
 | 
					    end: payload.event.end,
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onEventClick(payload: EventClickArg): void {
 | 
					function onEventClick(payload: EventClickArg): void {
 | 
				
			||||||
    // @ts-ignore TS does not recognize the target. But it does exists.
 | 
					  // @ts-ignore TS does not recognize the target. But it does exists.
 | 
				
			||||||
    if (payload.jsEvent.target.classList.contains("delete")) {
 | 
					  if (payload.jsEvent.target.classList.contains("delete")) {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (payload.event.extendedProps.is !== "range") {
 | 
					  if (payload.event.extendedProps.is !== "range") {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    editLocation.value?.startEdit(payload.event);
 | 
					  editLocation.value?.startEdit(payload.event);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function copyDay() {
 | 
					function copyDay() {
 | 
				
			||||||
    if (null === copyFrom.value || null === copyTo.value) {
 | 
					  if (null === copyFrom.value || null === copyTo.value) {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
 | 
					  store.dispatch("calendarRanges/copyFromDayToAnotherDay", {
 | 
				
			||||||
        from: ISOToDate(copyFrom.value),
 | 
					    from: ISOToDate(copyFrom.value),
 | 
				
			||||||
        to: ISOToDate(copyTo.value),
 | 
					    to: ISOToDate(copyTo.value),
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function copyWeek() {
 | 
					function copyWeek() {
 | 
				
			||||||
    if (null === copyFromWeek.value || null === copyToWeek.value) {
 | 
					  if (null === copyFromWeek.value || null === copyToWeek.value) {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
 | 
					  store.dispatch("calendarRanges/copyFromWeekToAnotherWeek", {
 | 
				
			||||||
        fromMonday: ISOToDate(copyFromWeek.value),
 | 
					    fromMonday: ISOToDate(copyFromWeek.value),
 | 
				
			||||||
        toMonday: ISOToDate(copyToWeek.value),
 | 
					    toMonday: ISOToDate(copyToWeek.value),
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
    copyFromWeek.value = dateToISO(getMonday(0));
 | 
					  copyFromWeek.value = dateToISO(getMonday(0));
 | 
				
			||||||
    copyToWeek.value = dateToISO(getMonday(1));
 | 
					  copyToWeek.value = dateToISO(getMonday(1));
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
#copy-widget {
 | 
					#copy-widget {
 | 
				
			||||||
    position: sticky;
 | 
					  position: sticky;
 | 
				
			||||||
    bottom: 0px;
 | 
					  bottom: 0px;
 | 
				
			||||||
    background-color: white;
 | 
					  background-color: white;
 | 
				
			||||||
    z-index: 9999999999;
 | 
					  z-index: 9999999999;
 | 
				
			||||||
    padding: 0.25rem 0 0.25rem;
 | 
					  padding: 0.25rem 0 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
div.copy-chevron {
 | 
					div.copy-chevron {
 | 
				
			||||||
    text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
    font-size: x-large;
 | 
					  font-size: x-large;
 | 
				
			||||||
    width: 2rem;
 | 
					  width: 2rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +1,28 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <component :is="Teleport" to="body">
 | 
					  <component :is="Teleport" to="body">
 | 
				
			||||||
        <modal v-if="showModal" @close="closeModal">
 | 
					    <modal v-if="showModal" @close="closeModal">
 | 
				
			||||||
            <template v-slot:header>
 | 
					      <template v-slot:header>
 | 
				
			||||||
                <h3>{{ "Modifier le lieu" }}</h3>
 | 
					        <h3>{{ "Modifier le lieu" }}</h3>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template v-slot:body>
 | 
					      <template v-slot:body>
 | 
				
			||||||
                <div></div>
 | 
					        <div></div>
 | 
				
			||||||
                <label>Localisation</label>
 | 
					        <label>Localisation</label>
 | 
				
			||||||
                <vue-multiselect
 | 
					        <vue-multiselect
 | 
				
			||||||
                    v-model="location"
 | 
					          v-model="location"
 | 
				
			||||||
                    :options="locations"
 | 
					          :options="locations"
 | 
				
			||||||
                    :label="'name'"
 | 
					          :label="'name'"
 | 
				
			||||||
                    :track-by="'id'"
 | 
					          :track-by="'id'"
 | 
				
			||||||
                ></vue-multiselect>
 | 
					        ></vue-multiselect>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <template v-slot:footer>
 | 
					      <template v-slot:footer>
 | 
				
			||||||
                <button class="btn btn-save" @click="saveAndClose">
 | 
					        <button class="btn btn-save" @click="saveAndClose">
 | 
				
			||||||
                    {{ "Enregistrer" }}
 | 
					          {{ "Enregistrer" }}
 | 
				
			||||||
                </button>
 | 
					        </button>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
        </modal>
 | 
					    </modal>
 | 
				
			||||||
    </component>
 | 
					  </component>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
@@ -39,7 +39,7 @@ import VueMultiselect from "vue-multiselect";
 | 
				
			|||||||
import { Teleport as teleport_, TeleportProps, VNodeProps } from "vue";
 | 
					import { Teleport as teleport_, TeleportProps, VNodeProps } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Teleport = teleport_ as new () => {
 | 
					const Teleport = teleport_ as new () => {
 | 
				
			||||||
    $props: VNodeProps & TeleportProps;
 | 
					  $props: VNodeProps & TeleportProps;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const store = useStore(key);
 | 
					const store = useStore(key);
 | 
				
			||||||
@@ -50,37 +50,37 @@ const showModal = ref(false);
 | 
				
			|||||||
//const tele = ref<InstanceType<typeof Teleport> | null>(null);
 | 
					//const tele = ref<InstanceType<typeof Teleport> | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const locations = computed<Location[]>(() => {
 | 
					const locations = computed<Location[]>(() => {
 | 
				
			||||||
    return store.state.locations.locations;
 | 
					  return store.state.locations.locations;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const startEdit = function (event: EventApi): void {
 | 
					const startEdit = function (event: EventApi): void {
 | 
				
			||||||
    console.log("startEditing", event);
 | 
					  console.log("startEditing", event);
 | 
				
			||||||
    calendarRangeId.value = event.extendedProps.calendarRangeId;
 | 
					  calendarRangeId.value = event.extendedProps.calendarRangeId;
 | 
				
			||||||
    location.value =
 | 
					  location.value =
 | 
				
			||||||
        store.getters["locations/getLocationById"](
 | 
					    store.getters["locations/getLocationById"](
 | 
				
			||||||
            event.extendedProps.locationId,
 | 
					      event.extendedProps.locationId,
 | 
				
			||||||
        ) || null;
 | 
					    ) || null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log("new location value", location.value);
 | 
					  console.log("new location value", location.value);
 | 
				
			||||||
    console.log("calendar range id", calendarRangeId.value);
 | 
					  console.log("calendar range id", calendarRangeId.value);
 | 
				
			||||||
    showModal.value = true;
 | 
					  showModal.value = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const saveAndClose = function (e: Event): void {
 | 
					const saveAndClose = function (e: Event): void {
 | 
				
			||||||
    console.log("saveEditAndClose", e);
 | 
					  console.log("saveEditAndClose", e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store
 | 
					  store
 | 
				
			||||||
        .dispatch("calendarRanges/patchRangeLocation", {
 | 
					    .dispatch("calendarRanges/patchRangeLocation", {
 | 
				
			||||||
            location: location.value,
 | 
					      location: location.value,
 | 
				
			||||||
            calendarRangeId: calendarRangeId.value,
 | 
					      calendarRangeId: calendarRangeId.value,
 | 
				
			||||||
        })
 | 
					    })
 | 
				
			||||||
        .then((_) => {
 | 
					    .then((_) => {
 | 
				
			||||||
            showModal.value = false;
 | 
					      showModal.value = false;
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const closeModal = function (_: any): void {
 | 
					const closeModal = function (_: any): void {
 | 
				
			||||||
    showModal.value = false;
 | 
					  showModal.value = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ startEdit });
 | 
					defineExpose({ startEdit });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,27 @@
 | 
				
			|||||||
const appMessages = {
 | 
					const appMessages = {
 | 
				
			||||||
    fr: {
 | 
					  fr: {
 | 
				
			||||||
        created_availabilities: "Lieu des plages de disponibilités créées",
 | 
					    created_availabilities: "Lieu des plages de disponibilités créées",
 | 
				
			||||||
        edit_your_calendar_range: "Planifiez vos plages de disponibilités",
 | 
					    edit_your_calendar_range: "Planifiez vos plages de disponibilités",
 | 
				
			||||||
        show_my_calendar: "Afficher mon calendrier",
 | 
					    show_my_calendar: "Afficher mon calendrier",
 | 
				
			||||||
        show_weekends: "Afficher les week-ends",
 | 
					    show_weekends: "Afficher les week-ends",
 | 
				
			||||||
        copy_range: "Copier",
 | 
					    copy_range: "Copier",
 | 
				
			||||||
        copy_range_from_to: "Copier les plages",
 | 
					    copy_range_from_to: "Copier les plages",
 | 
				
			||||||
        from_day_to_day: "d'un jour à l'autre",
 | 
					    from_day_to_day: "d'un jour à l'autre",
 | 
				
			||||||
        from_week_to_week: "d'une semaine à l'autre",
 | 
					    from_week_to_week: "d'une semaine à l'autre",
 | 
				
			||||||
        copy_range_how_to:
 | 
					    copy_range_how_to:
 | 
				
			||||||
            "Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
 | 
					      "Créez les plages de disponibilités durant une journée et copiez-les facilement au jour suivant avec ce bouton. Si les week-ends sont cachés, le jour suivant un vendredi sera le lundi.",
 | 
				
			||||||
        new_range_to_save: "Nouvelles plages à enregistrer",
 | 
					    new_range_to_save: "Nouvelles plages à enregistrer",
 | 
				
			||||||
        update_range_to_save: "Plages à modifier",
 | 
					    update_range_to_save: "Plages à modifier",
 | 
				
			||||||
        delete_range_to_save: "Plages à supprimer",
 | 
					    delete_range_to_save: "Plages à supprimer",
 | 
				
			||||||
        by: "Par",
 | 
					    by: "Par",
 | 
				
			||||||
        main_user_concerned: "Utilisateur concerné",
 | 
					    main_user_concerned: "Utilisateur concerné",
 | 
				
			||||||
        dateFrom: "De",
 | 
					    dateFrom: "De",
 | 
				
			||||||
        dateTo: "à",
 | 
					    dateTo: "à",
 | 
				
			||||||
        day: "Jour",
 | 
					    day: "Jour",
 | 
				
			||||||
        week: "Semaine",
 | 
					    week: "Semaine",
 | 
				
			||||||
        month: "Mois",
 | 
					    month: "Mois",
 | 
				
			||||||
        today: "Aujourd'hui",
 | 
					    today: "Aujourd'hui",
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { appMessages };
 | 
					export { appMessages };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,13 +7,13 @@ import App2 from "./App2.vue";
 | 
				
			|||||||
import { useI18n } from "vue-i18n";
 | 
					import { useI18n } from "vue-i18n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
futureStore().then((store) => {
 | 
					futureStore().then((store) => {
 | 
				
			||||||
    const i18n = _createI18n(appMessages, false);
 | 
					  const i18n = _createI18n(appMessages, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const app = createApp({
 | 
					  const app = createApp({
 | 
				
			||||||
        template: `<app></app>`,
 | 
					    template: `<app></app>`,
 | 
				
			||||||
    })
 | 
					  })
 | 
				
			||||||
        .use(store, key)
 | 
					    .use(store, key)
 | 
				
			||||||
        .use(i18n)
 | 
					    .use(i18n)
 | 
				
			||||||
        .component("app", App2)
 | 
					    .component("app", App2)
 | 
				
			||||||
        .mount("#myCalendar");
 | 
					    .mount("#myCalendar");
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@ import me, { MeState } from "./modules/me";
 | 
				
			|||||||
import fullCalendar, { FullCalendarState } from "./modules/fullcalendar";
 | 
					import fullCalendar, { FullCalendarState } from "./modules/fullcalendar";
 | 
				
			||||||
import calendarRanges, { CalendarRangesState } from "./modules/calendarRanges";
 | 
					import calendarRanges, { CalendarRangesState } from "./modules/calendarRanges";
 | 
				
			||||||
import calendarRemotes, {
 | 
					import calendarRemotes, {
 | 
				
			||||||
    CalendarRemotesState,
 | 
					  CalendarRemotesState,
 | 
				
			||||||
} from "./modules/calendarRemotes";
 | 
					} from "./modules/calendarRemotes";
 | 
				
			||||||
import { whoami } from "../../../../../../ChillMainBundle/Resources/public/lib/api/user";
 | 
					import { whoami } from "../../../../../../ChillMainBundle/Resources/public/lib/api/user";
 | 
				
			||||||
import { User } from "../../../../../../ChillMainBundle/Resources/public/types";
 | 
					import { User } from "../../../../../../ChillMainBundle/Resources/public/types";
 | 
				
			||||||
@@ -15,42 +15,40 @@ import calendarLocals, { CalendarLocalsState } from "./modules/calendarLocals";
 | 
				
			|||||||
const debug = process.env.NODE_ENV !== "production";
 | 
					const debug = process.env.NODE_ENV !== "production";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface State {
 | 
					export interface State {
 | 
				
			||||||
    calendarRanges: CalendarRangesState;
 | 
					  calendarRanges: CalendarRangesState;
 | 
				
			||||||
    calendarRemotes: CalendarRemotesState;
 | 
					  calendarRemotes: CalendarRemotesState;
 | 
				
			||||||
    calendarLocals: CalendarLocalsState;
 | 
					  calendarLocals: CalendarLocalsState;
 | 
				
			||||||
    fullCalendar: FullCalendarState;
 | 
					  fullCalendar: FullCalendarState;
 | 
				
			||||||
    me: MeState;
 | 
					  me: MeState;
 | 
				
			||||||
    locations: LocationState;
 | 
					  locations: LocationState;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const key: InjectionKey<Store<State>> = Symbol();
 | 
					export const key: InjectionKey<Store<State>> = Symbol();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const futureStore = function (): Promise<Store<State>> {
 | 
					const futureStore = function (): Promise<Store<State>> {
 | 
				
			||||||
    return whoami().then((user: User) => {
 | 
					  return whoami().then((user: User) => {
 | 
				
			||||||
        const store = createStore<State>({
 | 
					    const store = createStore<State>({
 | 
				
			||||||
            strict: debug,
 | 
					      strict: debug,
 | 
				
			||||||
            modules: {
 | 
					      modules: {
 | 
				
			||||||
                me,
 | 
					        me,
 | 
				
			||||||
                fullCalendar,
 | 
					        fullCalendar,
 | 
				
			||||||
                calendarRanges,
 | 
					        calendarRanges,
 | 
				
			||||||
                calendarRemotes,
 | 
					        calendarRemotes,
 | 
				
			||||||
                calendarLocals,
 | 
					        calendarLocals,
 | 
				
			||||||
                locations,
 | 
					        locations,
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
            mutations: {},
 | 
					      mutations: {},
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        store.commit("me/setWhoAmi", user, { root: true });
 | 
					 | 
				
			||||||
        store
 | 
					 | 
				
			||||||
            .dispatch("locations/getLocations", null, { root: true })
 | 
					 | 
				
			||||||
            .then((_) => {
 | 
					 | 
				
			||||||
                return store.dispatch("locations/getCurrentLocation", null, {
 | 
					 | 
				
			||||||
                    root: true,
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return Promise.resolve(store);
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    store.commit("me/setWhoAmi", user, { root: true });
 | 
				
			||||||
 | 
					    store.dispatch("locations/getLocations", null, { root: true }).then((_) => {
 | 
				
			||||||
 | 
					      return store.dispatch("locations/getCurrentLocation", null, {
 | 
				
			||||||
 | 
					        root: true,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Promise.resolve(store);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default futureStore;
 | 
					export default futureStore;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,109 +8,99 @@ import { TransportExceptionInterface } from "../../../../../../../ChillMainBundl
 | 
				
			|||||||
import { COLORS } from "../../../Calendar/const";
 | 
					import { COLORS } from "../../../Calendar/const";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarLocalsState {
 | 
					export interface CalendarLocalsState {
 | 
				
			||||||
    locals: EventInput[];
 | 
					  locals: EventInput[];
 | 
				
			||||||
    localsLoaded: { start: number; end: number }[];
 | 
					  localsLoaded: { start: number; end: number }[];
 | 
				
			||||||
    localsIndex: Set<string>;
 | 
					  localsIndex: Set<string>;
 | 
				
			||||||
    key: number;
 | 
					  key: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Context = ActionContext<CalendarLocalsState, State>;
 | 
					type Context = ActionContext<CalendarLocalsState, State>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): CalendarLocalsState => ({
 | 
					  state: (): CalendarLocalsState => ({
 | 
				
			||||||
        locals: [],
 | 
					    locals: [],
 | 
				
			||||||
        localsLoaded: [],
 | 
					    localsLoaded: [],
 | 
				
			||||||
        localsIndex: new Set<string>(),
 | 
					    localsIndex: new Set<string>(),
 | 
				
			||||||
        key: 0,
 | 
					    key: 0,
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
    getters: {
 | 
					  getters: {
 | 
				
			||||||
        isLocalsLoaded:
 | 
					    isLocalsLoaded:
 | 
				
			||||||
            (state: CalendarLocalsState) =>
 | 
					      (state: CalendarLocalsState) =>
 | 
				
			||||||
            ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
					      ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
				
			||||||
                for (const range of state.localsLoaded) {
 | 
					        for (const range of state.localsLoaded) {
 | 
				
			||||||
                    if (
 | 
					          if (start.getTime() === range.start && end.getTime() === range.end) {
 | 
				
			||||||
                        start.getTime() === range.start &&
 | 
					            return true;
 | 
				
			||||||
                        end.getTime() === range.end
 | 
					          }
 | 
				
			||||||
                    ) {
 | 
					        }
 | 
				
			||||||
                        return true;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return false;
 | 
					        return false;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mutations: {
 | 
				
			||||||
 | 
					    addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) {
 | 
				
			||||||
 | 
					      console.log("addLocals", ranges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const toAdd = ranges
 | 
				
			||||||
 | 
					        .map((cr) => localsToFullCalendarEvent(cr))
 | 
				
			||||||
 | 
					        .filter((r) => !state.localsIndex.has(r.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      toAdd.forEach((r) => {
 | 
				
			||||||
 | 
					        state.localsIndex.add(r.id);
 | 
				
			||||||
 | 
					        state.locals.push(r);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      state.key = state.key + toAdd.length;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mutations: {
 | 
					    addLoaded(state: CalendarLocalsState, payload: { start: Date; end: Date }) {
 | 
				
			||||||
        addLocals(state: CalendarLocalsState, ranges: CalendarLight[]) {
 | 
					      state.localsLoaded.push({
 | 
				
			||||||
            console.log("addLocals", ranges);
 | 
					        start: payload.start.getTime(),
 | 
				
			||||||
 | 
					        end: payload.end.getTime(),
 | 
				
			||||||
            const toAdd = ranges
 | 
					      });
 | 
				
			||||||
                .map((cr) => localsToFullCalendarEvent(cr))
 | 
					 | 
				
			||||||
                .filter((r) => !state.localsIndex.has(r.id));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            toAdd.forEach((r) => {
 | 
					 | 
				
			||||||
                state.localsIndex.add(r.id);
 | 
					 | 
				
			||||||
                state.locals.push(r);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            state.key = state.key + toAdd.length;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addLoaded(
 | 
					 | 
				
			||||||
            state: CalendarLocalsState,
 | 
					 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            state.localsLoaded.push({
 | 
					 | 
				
			||||||
                start: payload.start.getTime(),
 | 
					 | 
				
			||||||
                end: payload.end.getTime(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					  },
 | 
				
			||||||
        fetchLocals(
 | 
					  actions: {
 | 
				
			||||||
            ctx: Context,
 | 
					    fetchLocals(
 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					      ctx: Context,
 | 
				
			||||||
        ): Promise<null> {
 | 
					      payload: { start: Date; end: Date },
 | 
				
			||||||
            const start = payload.start;
 | 
					    ): Promise<null> {
 | 
				
			||||||
            const end = payload.end;
 | 
					      const start = payload.start;
 | 
				
			||||||
 | 
					      const end = payload.end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ctx.rootGetters["me/getMe"] === null) {
 | 
					      if (ctx.rootGetters["me/getMe"] === null) {
 | 
				
			||||||
                return Promise.resolve(null);
 | 
					        return Promise.resolve(null);
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ctx.getters.isLocalsLoaded({ start, end })) {
 | 
					      if (ctx.getters.isLocalsLoaded({ start, end })) {
 | 
				
			||||||
                return Promise.resolve(ctx.getters.getRangeSource);
 | 
					        return Promise.resolve(ctx.getters.getRangeSource);
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ctx.commit("addLoaded", {
 | 
					      ctx.commit("addLoaded", {
 | 
				
			||||||
                start: start,
 | 
					        start: start,
 | 
				
			||||||
                end: end,
 | 
					        end: end,
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return fetchCalendarLocalForUser(
 | 
					      return fetchCalendarLocalForUser(ctx.rootGetters["me/getMe"], start, end)
 | 
				
			||||||
                ctx.rootGetters["me/getMe"],
 | 
					        .then((remotes: CalendarLight[]) => {
 | 
				
			||||||
                start,
 | 
					          // to be add when reactivity problem will be solve ?
 | 
				
			||||||
                end,
 | 
					          //ctx.commit('addRemotes', remotes);
 | 
				
			||||||
            )
 | 
					          const inputs = remotes
 | 
				
			||||||
                .then((remotes: CalendarLight[]) => {
 | 
					            .map((cr) => localsToFullCalendarEvent(cr))
 | 
				
			||||||
                    // to be add when reactivity problem will be solve ?
 | 
					            .map((cr) => ({
 | 
				
			||||||
                    //ctx.commit('addRemotes', remotes);
 | 
					              ...cr,
 | 
				
			||||||
                    const inputs = remotes
 | 
					              backgroundColor: COLORS[0],
 | 
				
			||||||
                        .map((cr) => localsToFullCalendarEvent(cr))
 | 
					              textColor: "black",
 | 
				
			||||||
                        .map((cr) => ({
 | 
					              editable: false,
 | 
				
			||||||
                            ...cr,
 | 
					            }));
 | 
				
			||||||
                            backgroundColor: COLORS[0],
 | 
					          ctx.commit("calendarRanges/addExternals", inputs, {
 | 
				
			||||||
                            textColor: "black",
 | 
					            root: true,
 | 
				
			||||||
                            editable: false,
 | 
					          });
 | 
				
			||||||
                        }));
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
                    ctx.commit("calendarRanges/addExternals", inputs, {
 | 
					        })
 | 
				
			||||||
                        root: true,
 | 
					        .catch((e: TransportExceptionInterface) => {
 | 
				
			||||||
                    });
 | 
					          console.error(e);
 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .catch((e: TransportExceptionInterface) => {
 | 
					 | 
				
			||||||
                    console.error(e);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
                });
 | 
					        });
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
} as Module<CalendarLocalsState, State>;
 | 
					} as Module<CalendarLocalsState, State>;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
import { State } from "./../index";
 | 
					import { State } from "./../index";
 | 
				
			||||||
import { ActionContext, Module } from "vuex";
 | 
					import { ActionContext, Module } from "vuex";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    CalendarRange,
 | 
					  CalendarRange,
 | 
				
			||||||
    CalendarRangeCreate,
 | 
					  CalendarRangeCreate,
 | 
				
			||||||
    CalendarRangeEdit,
 | 
					  CalendarRangeEdit,
 | 
				
			||||||
    isEventInputCalendarRange,
 | 
					  isEventInputCalendarRange,
 | 
				
			||||||
} from "../../../../types";
 | 
					} from "../../../../types";
 | 
				
			||||||
import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types";
 | 
					import { Location } from "../../../../../../../ChillMainBundle/Resources/public/types";
 | 
				
			||||||
import { fetchCalendarRangeForUser } from "../../../Calendar/api";
 | 
					import { fetchCalendarRangeForUser } from "../../../Calendar/api";
 | 
				
			||||||
@@ -12,369 +12,332 @@ import { calendarRangeToFullCalendarEvent } from "../../../Calendar/store/utils"
 | 
				
			|||||||
import { EventInput } from "@fullcalendar/core";
 | 
					import { EventInput } from "@fullcalendar/core";
 | 
				
			||||||
import { makeFetch } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
					import { makeFetch } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    datetimeToISO,
 | 
					  datetimeToISO,
 | 
				
			||||||
    dateToISO,
 | 
					  dateToISO,
 | 
				
			||||||
    ISOToDatetime,
 | 
					  ISOToDatetime,
 | 
				
			||||||
} from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
					} from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
				
			||||||
import type { EventInputCalendarRange } from "../../../../types";
 | 
					import type { EventInputCalendarRange } from "../../../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRangesState {
 | 
					export interface CalendarRangesState {
 | 
				
			||||||
    ranges: (EventInput | EventInputCalendarRange)[];
 | 
					  ranges: (EventInput | EventInputCalendarRange)[];
 | 
				
			||||||
    rangesLoaded: { start: number; end: number }[];
 | 
					  rangesLoaded: { start: number; end: number }[];
 | 
				
			||||||
    rangesIndex: Set<string>;
 | 
					  rangesIndex: Set<string>;
 | 
				
			||||||
    key: number;
 | 
					  key: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Context = ActionContext<CalendarRangesState, State>;
 | 
					type Context = ActionContext<CalendarRangesState, State>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): CalendarRangesState => ({
 | 
					  state: (): CalendarRangesState => ({
 | 
				
			||||||
        ranges: [],
 | 
					    ranges: [],
 | 
				
			||||||
        rangesLoaded: [],
 | 
					    rangesLoaded: [],
 | 
				
			||||||
        rangesIndex: new Set<string>(),
 | 
					    rangesIndex: new Set<string>(),
 | 
				
			||||||
        key: 0,
 | 
					    key: 0,
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
    getters: {
 | 
					  getters: {
 | 
				
			||||||
        isRangeLoaded:
 | 
					    isRangeLoaded:
 | 
				
			||||||
            (state: CalendarRangesState) =>
 | 
					      (state: CalendarRangesState) =>
 | 
				
			||||||
            ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
					      ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
				
			||||||
                for (const range of state.rangesLoaded) {
 | 
					        for (const range of state.rangesLoaded) {
 | 
				
			||||||
                    if (
 | 
					          if (start.getTime() === range.start && end.getTime() === range.end) {
 | 
				
			||||||
                        start.getTime() === range.start &&
 | 
					            return true;
 | 
				
			||||||
                        end.getTime() === range.end
 | 
					          }
 | 
				
			||||||
                    ) {
 | 
					        }
 | 
				
			||||||
                        return true;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return false;
 | 
					        return false;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        getRangesOnDate:
 | 
					    getRangesOnDate:
 | 
				
			||||||
            (state: CalendarRangesState) =>
 | 
					      (state: CalendarRangesState) =>
 | 
				
			||||||
            (date: Date): EventInputCalendarRange[] => {
 | 
					      (date: Date): EventInputCalendarRange[] => {
 | 
				
			||||||
                const founds = [];
 | 
					        const founds = [];
 | 
				
			||||||
                const dateStr = dateToISO(date) as string;
 | 
					        const dateStr = dateToISO(date) as string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const range of state.ranges) {
 | 
					        for (const range of state.ranges) {
 | 
				
			||||||
                    if (
 | 
					          if (
 | 
				
			||||||
                        isEventInputCalendarRange(range) &&
 | 
					            isEventInputCalendarRange(range) &&
 | 
				
			||||||
                        range.start.startsWith(dateStr)
 | 
					            range.start.startsWith(dateStr)
 | 
				
			||||||
                    ) {
 | 
					          ) {
 | 
				
			||||||
                        founds.push(range);
 | 
					            founds.push(range);
 | 
				
			||||||
                    }
 | 
					          }
 | 
				
			||||||
                }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return founds;
 | 
					        return founds;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        getRangesOnWeek:
 | 
					    getRangesOnWeek:
 | 
				
			||||||
            (state: CalendarRangesState) =>
 | 
					      (state: CalendarRangesState) =>
 | 
				
			||||||
            (mondayDate: Date): EventInputCalendarRange[] => {
 | 
					      (mondayDate: Date): EventInputCalendarRange[] => {
 | 
				
			||||||
                const founds = [];
 | 
					        const founds = [];
 | 
				
			||||||
                for (const d of Array.from(Array(7).keys())) {
 | 
					        for (const d of Array.from(Array(7).keys())) {
 | 
				
			||||||
                    const dateOfWeek = new Date(mondayDate);
 | 
					          const dateOfWeek = new Date(mondayDate);
 | 
				
			||||||
                    dateOfWeek.setDate(mondayDate.getDate() + d);
 | 
					          dateOfWeek.setDate(mondayDate.getDate() + d);
 | 
				
			||||||
                    const dateStr = dateToISO(dateOfWeek) as string;
 | 
					          const dateStr = dateToISO(dateOfWeek) as string;
 | 
				
			||||||
                    for (const range of state.ranges) {
 | 
					          for (const range of state.ranges) {
 | 
				
			||||||
                        if (
 | 
					            if (
 | 
				
			||||||
                            isEventInputCalendarRange(range) &&
 | 
					              isEventInputCalendarRange(range) &&
 | 
				
			||||||
                            range.start.startsWith(dateStr)
 | 
					              range.start.startsWith(dateStr)
 | 
				
			||||||
                        ) {
 | 
					            ) {
 | 
				
			||||||
                            founds.push(range);
 | 
					              founds.push(range);
 | 
				
			||||||
                        }
 | 
					            }
 | 
				
			||||||
                    }
 | 
					          }
 | 
				
			||||||
                }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return founds;
 | 
					        return founds;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mutations: {
 | 
				
			||||||
 | 
					    addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
 | 
				
			||||||
 | 
					      const toAdd = ranges
 | 
				
			||||||
 | 
					        .map((cr) => calendarRangeToFullCalendarEvent(cr))
 | 
				
			||||||
 | 
					        .map((cr) => ({
 | 
				
			||||||
 | 
					          ...cr,
 | 
				
			||||||
 | 
					          backgroundColor: "white",
 | 
				
			||||||
 | 
					          borderColor: "#3788d8",
 | 
				
			||||||
 | 
					          textColor: "black",
 | 
				
			||||||
 | 
					        }))
 | 
				
			||||||
 | 
					        .filter((r) => !state.rangesIndex.has(r.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      toAdd.forEach((r) => {
 | 
				
			||||||
 | 
					        state.rangesIndex.add(r.id);
 | 
				
			||||||
 | 
					        state.ranges.push(r);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      state.key = state.key + toAdd.length;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mutations: {
 | 
					    addExternals(
 | 
				
			||||||
        addRanges(state: CalendarRangesState, ranges: CalendarRange[]) {
 | 
					      state: CalendarRangesState,
 | 
				
			||||||
            const toAdd = ranges
 | 
					      externalEvents: (EventInput & { id: string })[],
 | 
				
			||||||
                .map((cr) => calendarRangeToFullCalendarEvent(cr))
 | 
					    ) {
 | 
				
			||||||
                .map((cr) => ({
 | 
					      const toAdd = externalEvents.filter((r) => !state.rangesIndex.has(r.id));
 | 
				
			||||||
                    ...cr,
 | 
					 | 
				
			||||||
                    backgroundColor: "white",
 | 
					 | 
				
			||||||
                    borderColor: "#3788d8",
 | 
					 | 
				
			||||||
                    textColor: "black",
 | 
					 | 
				
			||||||
                }))
 | 
					 | 
				
			||||||
                .filter((r) => !state.rangesIndex.has(r.id));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            toAdd.forEach((r) => {
 | 
					      toAdd.forEach((r) => {
 | 
				
			||||||
                state.rangesIndex.add(r.id);
 | 
					        state.rangesIndex.add(r.id);
 | 
				
			||||||
                state.ranges.push(r);
 | 
					        state.ranges.push(r);
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
            state.key = state.key + toAdd.length;
 | 
					      state.key = state.key + toAdd.length;
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addExternals(
 | 
					 | 
				
			||||||
            state: CalendarRangesState,
 | 
					 | 
				
			||||||
            externalEvents: (EventInput & { id: string })[],
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            const toAdd = externalEvents.filter(
 | 
					 | 
				
			||||||
                (r) => !state.rangesIndex.has(r.id),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            toAdd.forEach((r) => {
 | 
					 | 
				
			||||||
                state.rangesIndex.add(r.id);
 | 
					 | 
				
			||||||
                state.ranges.push(r);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            state.key = state.key + toAdd.length;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addLoaded(
 | 
					 | 
				
			||||||
            state: CalendarRangesState,
 | 
					 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            state.rangesLoaded.push({
 | 
					 | 
				
			||||||
                start: payload.start.getTime(),
 | 
					 | 
				
			||||||
                end: payload.end.getTime(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addRange(state: CalendarRangesState, payload: CalendarRange) {
 | 
					 | 
				
			||||||
            const asEvent = calendarRangeToFullCalendarEvent(payload);
 | 
					 | 
				
			||||||
            state.ranges.push({
 | 
					 | 
				
			||||||
                ...asEvent,
 | 
					 | 
				
			||||||
                backgroundColor: "white",
 | 
					 | 
				
			||||||
                borderColor: "#3788d8",
 | 
					 | 
				
			||||||
                textColor: "black",
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            state.rangesIndex.add(asEvent.id);
 | 
					 | 
				
			||||||
            state.key = state.key + 1;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        removeRange(state: CalendarRangesState, calendarRangeId: number) {
 | 
					 | 
				
			||||||
            const found = state.ranges.find(
 | 
					 | 
				
			||||||
                (r) =>
 | 
					 | 
				
			||||||
                    r.calendarRangeId === calendarRangeId && r.is === "range",
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (found !== undefined) {
 | 
					 | 
				
			||||||
                state.ranges = state.ranges.filter(
 | 
					 | 
				
			||||||
                    (r) =>
 | 
					 | 
				
			||||||
                        !(
 | 
					 | 
				
			||||||
                            r.calendarRangeId === calendarRangeId &&
 | 
					 | 
				
			||||||
                            r.is === "range"
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (typeof found.id === "string") {
 | 
					 | 
				
			||||||
                    // should always be true
 | 
					 | 
				
			||||||
                    state.rangesIndex.delete(found.id);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                state.key = state.key + 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        updateRange(state: CalendarRangesState, range: CalendarRange) {
 | 
					 | 
				
			||||||
            const found = state.ranges.find(
 | 
					 | 
				
			||||||
                (r) => r.calendarRangeId === range.id && r.is === "range",
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            const newEvent = calendarRangeToFullCalendarEvent(range);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (found !== undefined) {
 | 
					 | 
				
			||||||
                found.start = newEvent.start;
 | 
					 | 
				
			||||||
                found.end = newEvent.end;
 | 
					 | 
				
			||||||
                found.locationId = range.location.id;
 | 
					 | 
				
			||||||
                found.locationName = range.location.name;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            state.key = state.key + 1;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					    addLoaded(state: CalendarRangesState, payload: { start: Date; end: Date }) {
 | 
				
			||||||
        fetchRanges(
 | 
					      state.rangesLoaded.push({
 | 
				
			||||||
            ctx: Context,
 | 
					        start: payload.start.getTime(),
 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					        end: payload.end.getTime(),
 | 
				
			||||||
        ): Promise<null> {
 | 
					      });
 | 
				
			||||||
            const start = payload.start;
 | 
					 | 
				
			||||||
            const end = payload.end;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (ctx.rootGetters["me/getMe"] === null) {
 | 
					 | 
				
			||||||
                return Promise.resolve(ctx.getters.getRangeSource);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (ctx.getters.isRangeLoaded({ start, end })) {
 | 
					 | 
				
			||||||
                return Promise.resolve(ctx.getters.getRangeSource);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ctx.commit("addLoaded", {
 | 
					 | 
				
			||||||
                start: start,
 | 
					 | 
				
			||||||
                end: end,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return fetchCalendarRangeForUser(
 | 
					 | 
				
			||||||
                ctx.rootGetters["me/getMe"],
 | 
					 | 
				
			||||||
                start,
 | 
					 | 
				
			||||||
                end,
 | 
					 | 
				
			||||||
            ).then((ranges: CalendarRange[]) => {
 | 
					 | 
				
			||||||
                ctx.commit("addRanges", ranges);
 | 
					 | 
				
			||||||
                return Promise.resolve(null);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        createRange(
 | 
					 | 
				
			||||||
            ctx: Context,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                start,
 | 
					 | 
				
			||||||
                end,
 | 
					 | 
				
			||||||
                location,
 | 
					 | 
				
			||||||
            }: { start: Date; end: Date; location: Location },
 | 
					 | 
				
			||||||
        ): Promise<null> {
 | 
					 | 
				
			||||||
            const url = `/api/1.0/calendar/calendar-range.json?`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (ctx.rootState.me.me === null) {
 | 
					 | 
				
			||||||
                throw new Error("user is currently null");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const body = {
 | 
					 | 
				
			||||||
                user: {
 | 
					 | 
				
			||||||
                    id: ctx.rootState.me.me.id,
 | 
					 | 
				
			||||||
                    type: "user",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                startDate: {
 | 
					 | 
				
			||||||
                    datetime: datetimeToISO(start),
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                endDate: {
 | 
					 | 
				
			||||||
                    datetime: datetimeToISO(end),
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                location: {
 | 
					 | 
				
			||||||
                    id: location.id,
 | 
					 | 
				
			||||||
                    type: "location",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            } as CalendarRangeCreate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return makeFetch<CalendarRangeCreate, CalendarRange>(
 | 
					 | 
				
			||||||
                "POST",
 | 
					 | 
				
			||||||
                url,
 | 
					 | 
				
			||||||
                body,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
                .then((newRange) => {
 | 
					 | 
				
			||||||
                    ctx.commit("addRange", newRange);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .catch((error) => {
 | 
					 | 
				
			||||||
                    console.error(error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    throw error;
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        deleteRange(ctx: Context, calendarRangeId: number) {
 | 
					 | 
				
			||||||
            const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            makeFetch<undefined, never>("DELETE", url).then(() => {
 | 
					 | 
				
			||||||
                ctx.commit("removeRange", calendarRangeId);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        patchRangeTime(
 | 
					 | 
				
			||||||
            ctx,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                calendarRangeId,
 | 
					 | 
				
			||||||
                start,
 | 
					 | 
				
			||||||
                end,
 | 
					 | 
				
			||||||
            }: { calendarRangeId: number; start: Date; end: Date },
 | 
					 | 
				
			||||||
        ): Promise<null> {
 | 
					 | 
				
			||||||
            const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
					 | 
				
			||||||
            const body = {
 | 
					 | 
				
			||||||
                startDate: {
 | 
					 | 
				
			||||||
                    datetime: datetimeToISO(start),
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                endDate: {
 | 
					 | 
				
			||||||
                    datetime: datetimeToISO(end),
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            } as CalendarRangeEdit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return makeFetch<CalendarRangeEdit, CalendarRange>(
 | 
					 | 
				
			||||||
                "PATCH",
 | 
					 | 
				
			||||||
                url,
 | 
					 | 
				
			||||||
                body,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
                .then((range) => {
 | 
					 | 
				
			||||||
                    ctx.commit("updateRange", range);
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .catch((error) => {
 | 
					 | 
				
			||||||
                    console.error(error);
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        patchRangeLocation(
 | 
					 | 
				
			||||||
            ctx,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                location,
 | 
					 | 
				
			||||||
                calendarRangeId,
 | 
					 | 
				
			||||||
            }: { location: Location; calendarRangeId: number },
 | 
					 | 
				
			||||||
        ): Promise<null> {
 | 
					 | 
				
			||||||
            const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
					 | 
				
			||||||
            const body = {
 | 
					 | 
				
			||||||
                location: {
 | 
					 | 
				
			||||||
                    id: location.id,
 | 
					 | 
				
			||||||
                    type: "location",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            } as CalendarRangeEdit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return makeFetch<CalendarRangeEdit, CalendarRange>(
 | 
					 | 
				
			||||||
                "PATCH",
 | 
					 | 
				
			||||||
                url,
 | 
					 | 
				
			||||||
                body,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
                .then((range) => {
 | 
					 | 
				
			||||||
                    ctx.commit("updateRange", range);
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .catch((error) => {
 | 
					 | 
				
			||||||
                    console.error(error);
 | 
					 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        copyFromDayToAnotherDay(
 | 
					 | 
				
			||||||
            ctx,
 | 
					 | 
				
			||||||
            { from, to }: { from: Date; to: Date },
 | 
					 | 
				
			||||||
        ): Promise<null> {
 | 
					 | 
				
			||||||
            const rangesToCopy: EventInputCalendarRange[] =
 | 
					 | 
				
			||||||
                ctx.getters["getRangesOnDate"](from);
 | 
					 | 
				
			||||||
            const promises = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (const r of rangesToCopy) {
 | 
					 | 
				
			||||||
                const start = new Date(ISOToDatetime(r.start) as Date);
 | 
					 | 
				
			||||||
                start.setFullYear(
 | 
					 | 
				
			||||||
                    to.getFullYear(),
 | 
					 | 
				
			||||||
                    to.getMonth(),
 | 
					 | 
				
			||||||
                    to.getDate(),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                const end = new Date(ISOToDatetime(r.end) as Date);
 | 
					 | 
				
			||||||
                end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
 | 
					 | 
				
			||||||
                const location = ctx.rootGetters["locations/getLocationById"](
 | 
					 | 
				
			||||||
                    r.locationId,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                promises.push(
 | 
					 | 
				
			||||||
                    ctx.dispatch("createRange", { start, end, location }),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Promise.all(promises).then(() => Promise.resolve(null));
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        copyFromWeekToAnotherWeek(
 | 
					 | 
				
			||||||
            ctx: Context,
 | 
					 | 
				
			||||||
            { fromMonday, toMonday }: { fromMonday: Date; toMonday: Date },
 | 
					 | 
				
			||||||
        ): Promise<null> {
 | 
					 | 
				
			||||||
            const rangesToCopy: EventInputCalendarRange[] =
 | 
					 | 
				
			||||||
                ctx.getters["getRangesOnWeek"](fromMonday);
 | 
					 | 
				
			||||||
            const promises = [];
 | 
					 | 
				
			||||||
            const diffTime = toMonday.getTime() - fromMonday.getTime();
 | 
					 | 
				
			||||||
            for (const r of rangesToCopy) {
 | 
					 | 
				
			||||||
                const start = new Date(ISOToDatetime(r.start) as Date);
 | 
					 | 
				
			||||||
                const end = new Date(ISOToDatetime(r.end) as Date);
 | 
					 | 
				
			||||||
                start.setTime(start.getTime() + diffTime);
 | 
					 | 
				
			||||||
                end.setTime(end.getTime() + diffTime);
 | 
					 | 
				
			||||||
                const location = ctx.rootGetters["locations/getLocationById"](
 | 
					 | 
				
			||||||
                    r.locationId,
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                promises.push(
 | 
					 | 
				
			||||||
                    ctx.dispatch("createRange", { start, end, location }),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Promise.all(promises).then(() => Promise.resolve(null));
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    addRange(state: CalendarRangesState, payload: CalendarRange) {
 | 
				
			||||||
 | 
					      const asEvent = calendarRangeToFullCalendarEvent(payload);
 | 
				
			||||||
 | 
					      state.ranges.push({
 | 
				
			||||||
 | 
					        ...asEvent,
 | 
				
			||||||
 | 
					        backgroundColor: "white",
 | 
				
			||||||
 | 
					        borderColor: "#3788d8",
 | 
				
			||||||
 | 
					        textColor: "black",
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      state.rangesIndex.add(asEvent.id);
 | 
				
			||||||
 | 
					      state.key = state.key + 1;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    removeRange(state: CalendarRangesState, calendarRangeId: number) {
 | 
				
			||||||
 | 
					      const found = state.ranges.find(
 | 
				
			||||||
 | 
					        (r) => r.calendarRangeId === calendarRangeId && r.is === "range",
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (found !== undefined) {
 | 
				
			||||||
 | 
					        state.ranges = state.ranges.filter(
 | 
				
			||||||
 | 
					          (r) => !(r.calendarRangeId === calendarRangeId && r.is === "range"),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (typeof found.id === "string") {
 | 
				
			||||||
 | 
					          // should always be true
 | 
				
			||||||
 | 
					          state.rangesIndex.delete(found.id);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        state.key = state.key + 1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    updateRange(state: CalendarRangesState, range: CalendarRange) {
 | 
				
			||||||
 | 
					      const found = state.ranges.find(
 | 
				
			||||||
 | 
					        (r) => r.calendarRangeId === range.id && r.is === "range",
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      const newEvent = calendarRangeToFullCalendarEvent(range);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (found !== undefined) {
 | 
				
			||||||
 | 
					        found.start = newEvent.start;
 | 
				
			||||||
 | 
					        found.end = newEvent.end;
 | 
				
			||||||
 | 
					        found.locationId = range.location.id;
 | 
				
			||||||
 | 
					        found.locationName = range.location.name;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      state.key = state.key + 1;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  actions: {
 | 
				
			||||||
 | 
					    fetchRanges(
 | 
				
			||||||
 | 
					      ctx: Context,
 | 
				
			||||||
 | 
					      payload: { start: Date; end: Date },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const start = payload.start;
 | 
				
			||||||
 | 
					      const end = payload.end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ctx.rootGetters["me/getMe"] === null) {
 | 
				
			||||||
 | 
					        return Promise.resolve(ctx.getters.getRangeSource);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ctx.getters.isRangeLoaded({ start, end })) {
 | 
				
			||||||
 | 
					        return Promise.resolve(ctx.getters.getRangeSource);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ctx.commit("addLoaded", {
 | 
				
			||||||
 | 
					        start: start,
 | 
				
			||||||
 | 
					        end: end,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return fetchCalendarRangeForUser(
 | 
				
			||||||
 | 
					        ctx.rootGetters["me/getMe"],
 | 
				
			||||||
 | 
					        start,
 | 
				
			||||||
 | 
					        end,
 | 
				
			||||||
 | 
					      ).then((ranges: CalendarRange[]) => {
 | 
				
			||||||
 | 
					        ctx.commit("addRanges", ranges);
 | 
				
			||||||
 | 
					        return Promise.resolve(null);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    createRange(
 | 
				
			||||||
 | 
					      ctx: Context,
 | 
				
			||||||
 | 
					      { start, end, location }: { start: Date; end: Date; location: Location },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const url = `/api/1.0/calendar/calendar-range.json?`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (ctx.rootState.me.me === null) {
 | 
				
			||||||
 | 
					        throw new Error("user is currently null");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const body = {
 | 
				
			||||||
 | 
					        user: {
 | 
				
			||||||
 | 
					          id: ctx.rootState.me.me.id,
 | 
				
			||||||
 | 
					          type: "user",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        startDate: {
 | 
				
			||||||
 | 
					          datetime: datetimeToISO(start),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        endDate: {
 | 
				
			||||||
 | 
					          datetime: datetimeToISO(end),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        location: {
 | 
				
			||||||
 | 
					          id: location.id,
 | 
				
			||||||
 | 
					          type: "location",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      } as CalendarRangeCreate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return makeFetch<CalendarRangeCreate, CalendarRange>("POST", url, body)
 | 
				
			||||||
 | 
					        .then((newRange) => {
 | 
				
			||||||
 | 
					          ctx.commit("addRange", newRange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error(error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          throw error;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    deleteRange(ctx: Context, calendarRangeId: number) {
 | 
				
			||||||
 | 
					      const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      makeFetch<undefined, never>("DELETE", url).then(() => {
 | 
				
			||||||
 | 
					        ctx.commit("removeRange", calendarRangeId);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    patchRangeTime(
 | 
				
			||||||
 | 
					      ctx,
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        calendarRangeId,
 | 
				
			||||||
 | 
					        start,
 | 
				
			||||||
 | 
					        end,
 | 
				
			||||||
 | 
					      }: { calendarRangeId: number; start: Date; end: Date },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
				
			||||||
 | 
					      const body = {
 | 
				
			||||||
 | 
					        startDate: {
 | 
				
			||||||
 | 
					          datetime: datetimeToISO(start),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        endDate: {
 | 
				
			||||||
 | 
					          datetime: datetimeToISO(end),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      } as CalendarRangeEdit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return makeFetch<CalendarRangeEdit, CalendarRange>("PATCH", url, body)
 | 
				
			||||||
 | 
					        .then((range) => {
 | 
				
			||||||
 | 
					          ctx.commit("updateRange", range);
 | 
				
			||||||
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error(error);
 | 
				
			||||||
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    patchRangeLocation(
 | 
				
			||||||
 | 
					      ctx,
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        location,
 | 
				
			||||||
 | 
					        calendarRangeId,
 | 
				
			||||||
 | 
					      }: { location: Location; calendarRangeId: number },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const url = `/api/1.0/calendar/calendar-range/${calendarRangeId}.json`;
 | 
				
			||||||
 | 
					      const body = {
 | 
				
			||||||
 | 
					        location: {
 | 
				
			||||||
 | 
					          id: location.id,
 | 
				
			||||||
 | 
					          type: "location",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      } as CalendarRangeEdit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return makeFetch<CalendarRangeEdit, CalendarRange>("PATCH", url, body)
 | 
				
			||||||
 | 
					        .then((range) => {
 | 
				
			||||||
 | 
					          ctx.commit("updateRange", range);
 | 
				
			||||||
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          console.error(error);
 | 
				
			||||||
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    copyFromDayToAnotherDay(
 | 
				
			||||||
 | 
					      ctx,
 | 
				
			||||||
 | 
					      { from, to }: { from: Date; to: Date },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const rangesToCopy: EventInputCalendarRange[] =
 | 
				
			||||||
 | 
					        ctx.getters["getRangesOnDate"](from);
 | 
				
			||||||
 | 
					      const promises = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (const r of rangesToCopy) {
 | 
				
			||||||
 | 
					        const start = new Date(ISOToDatetime(r.start) as Date);
 | 
				
			||||||
 | 
					        start.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
 | 
				
			||||||
 | 
					        const end = new Date(ISOToDatetime(r.end) as Date);
 | 
				
			||||||
 | 
					        end.setFullYear(to.getFullYear(), to.getMonth(), to.getDate());
 | 
				
			||||||
 | 
					        const location = ctx.rootGetters["locations/getLocationById"](
 | 
				
			||||||
 | 
					          r.locationId,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(ctx.dispatch("createRange", { start, end, location }));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Promise.all(promises).then(() => Promise.resolve(null));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    copyFromWeekToAnotherWeek(
 | 
				
			||||||
 | 
					      ctx: Context,
 | 
				
			||||||
 | 
					      { fromMonday, toMonday }: { fromMonday: Date; toMonday: Date },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      const rangesToCopy: EventInputCalendarRange[] =
 | 
				
			||||||
 | 
					        ctx.getters["getRangesOnWeek"](fromMonday);
 | 
				
			||||||
 | 
					      const promises = [];
 | 
				
			||||||
 | 
					      const diffTime = toMonday.getTime() - fromMonday.getTime();
 | 
				
			||||||
 | 
					      for (const r of rangesToCopy) {
 | 
				
			||||||
 | 
					        const start = new Date(ISOToDatetime(r.start) as Date);
 | 
				
			||||||
 | 
					        const end = new Date(ISOToDatetime(r.end) as Date);
 | 
				
			||||||
 | 
					        start.setTime(start.getTime() + diffTime);
 | 
				
			||||||
 | 
					        end.setTime(end.getTime() + diffTime);
 | 
				
			||||||
 | 
					        const location = ctx.rootGetters["locations/getLocationById"](
 | 
				
			||||||
 | 
					          r.locationId,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(ctx.dispatch("createRange", { start, end, location }));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return Promise.all(promises).then(() => Promise.resolve(null));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
} as Module<CalendarRangesState, State>;
 | 
					} as Module<CalendarRangesState, State>;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,109 +8,102 @@ import { TransportExceptionInterface } from "../../../../../../../ChillMainBundl
 | 
				
			|||||||
import { COLORS } from "../../../Calendar/const";
 | 
					import { COLORS } from "../../../Calendar/const";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CalendarRemotesState {
 | 
					export interface CalendarRemotesState {
 | 
				
			||||||
    remotes: EventInput[];
 | 
					  remotes: EventInput[];
 | 
				
			||||||
    remotesLoaded: { start: number; end: number }[];
 | 
					  remotesLoaded: { start: number; end: number }[];
 | 
				
			||||||
    remotesIndex: Set<string>;
 | 
					  remotesIndex: Set<string>;
 | 
				
			||||||
    key: number;
 | 
					  key: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Context = ActionContext<CalendarRemotesState, State>;
 | 
					type Context = ActionContext<CalendarRemotesState, State>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): CalendarRemotesState => ({
 | 
					  state: (): CalendarRemotesState => ({
 | 
				
			||||||
        remotes: [],
 | 
					    remotes: [],
 | 
				
			||||||
        remotesLoaded: [],
 | 
					    remotesLoaded: [],
 | 
				
			||||||
        remotesIndex: new Set<string>(),
 | 
					    remotesIndex: new Set<string>(),
 | 
				
			||||||
        key: 0,
 | 
					    key: 0,
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
    getters: {
 | 
					  getters: {
 | 
				
			||||||
        isRemotesLoaded:
 | 
					    isRemotesLoaded:
 | 
				
			||||||
            (state: CalendarRemotesState) =>
 | 
					      (state: CalendarRemotesState) =>
 | 
				
			||||||
            ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
					      ({ start, end }: { start: Date; end: Date }): boolean => {
 | 
				
			||||||
                for (const range of state.remotesLoaded) {
 | 
					        for (const range of state.remotesLoaded) {
 | 
				
			||||||
                    if (
 | 
					          if (start.getTime() === range.start && end.getTime() === range.end) {
 | 
				
			||||||
                        start.getTime() === range.start &&
 | 
					            return true;
 | 
				
			||||||
                        end.getTime() === range.end
 | 
					          }
 | 
				
			||||||
                    ) {
 | 
					        }
 | 
				
			||||||
                        return true;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return false;
 | 
					        return false;
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mutations: {
 | 
				
			||||||
 | 
					    addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) {
 | 
				
			||||||
 | 
					      console.log("addRemotes", ranges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const toAdd = ranges
 | 
				
			||||||
 | 
					        .map((cr) => remoteToFullCalendarEvent(cr))
 | 
				
			||||||
 | 
					        .filter((r) => !state.remotesIndex.has(r.id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      toAdd.forEach((r) => {
 | 
				
			||||||
 | 
					        state.remotesIndex.add(r.id);
 | 
				
			||||||
 | 
					        state.remotes.push(r);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      state.key = state.key + toAdd.length;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mutations: {
 | 
					    addLoaded(
 | 
				
			||||||
        addRemotes(state: CalendarRemotesState, ranges: CalendarRemote[]) {
 | 
					      state: CalendarRemotesState,
 | 
				
			||||||
            console.log("addRemotes", ranges);
 | 
					      payload: { start: Date; end: Date },
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
            const toAdd = ranges
 | 
					      state.remotesLoaded.push({
 | 
				
			||||||
                .map((cr) => remoteToFullCalendarEvent(cr))
 | 
					        start: payload.start.getTime(),
 | 
				
			||||||
                .filter((r) => !state.remotesIndex.has(r.id));
 | 
					        end: payload.end.getTime(),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
            toAdd.forEach((r) => {
 | 
					 | 
				
			||||||
                state.remotesIndex.add(r.id);
 | 
					 | 
				
			||||||
                state.remotes.push(r);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            state.key = state.key + toAdd.length;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        addLoaded(
 | 
					 | 
				
			||||||
            state: CalendarRemotesState,
 | 
					 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            state.remotesLoaded.push({
 | 
					 | 
				
			||||||
                start: payload.start.getTime(),
 | 
					 | 
				
			||||||
                end: payload.end.getTime(),
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					  },
 | 
				
			||||||
        fetchRemotes(
 | 
					  actions: {
 | 
				
			||||||
            ctx: Context,
 | 
					    fetchRemotes(
 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					      ctx: Context,
 | 
				
			||||||
        ): Promise<null> {
 | 
					      payload: { start: Date; end: Date },
 | 
				
			||||||
            const start = payload.start;
 | 
					    ): Promise<null> {
 | 
				
			||||||
            const end = payload.end;
 | 
					      const start = payload.start;
 | 
				
			||||||
 | 
					      const end = payload.end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ctx.rootGetters["me/getMe"] === null) {
 | 
					      if (ctx.rootGetters["me/getMe"] === null) {
 | 
				
			||||||
                return Promise.resolve(null);
 | 
					        return Promise.resolve(null);
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ctx.getters.isRemotesLoaded({ start, end })) {
 | 
					      if (ctx.getters.isRemotesLoaded({ start, end })) {
 | 
				
			||||||
                return Promise.resolve(ctx.getters.getRangeSource);
 | 
					        return Promise.resolve(ctx.getters.getRangeSource);
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ctx.commit("addLoaded", {
 | 
					      ctx.commit("addLoaded", {
 | 
				
			||||||
                start: start,
 | 
					        start: start,
 | 
				
			||||||
                end: end,
 | 
					        end: end,
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return fetchCalendarRemoteForUser(
 | 
					      return fetchCalendarRemoteForUser(ctx.rootGetters["me/getMe"], start, end)
 | 
				
			||||||
                ctx.rootGetters["me/getMe"],
 | 
					        .then((remotes: CalendarRemote[]) => {
 | 
				
			||||||
                start,
 | 
					          // to be add when reactivity problem will be solve ?
 | 
				
			||||||
                end,
 | 
					          //ctx.commit('addRemotes', remotes);
 | 
				
			||||||
            )
 | 
					          const inputs = remotes
 | 
				
			||||||
                .then((remotes: CalendarRemote[]) => {
 | 
					            .map((cr) => remoteToFullCalendarEvent(cr))
 | 
				
			||||||
                    // to be add when reactivity problem will be solve ?
 | 
					            .map((cr) => ({
 | 
				
			||||||
                    //ctx.commit('addRemotes', remotes);
 | 
					              ...cr,
 | 
				
			||||||
                    const inputs = remotes
 | 
					              backgroundColor: COLORS[0],
 | 
				
			||||||
                        .map((cr) => remoteToFullCalendarEvent(cr))
 | 
					              textColor: "black",
 | 
				
			||||||
                        .map((cr) => ({
 | 
					              editable: false,
 | 
				
			||||||
                            ...cr,
 | 
					            }));
 | 
				
			||||||
                            backgroundColor: COLORS[0],
 | 
					          ctx.commit("calendarRanges/addExternals", inputs, {
 | 
				
			||||||
                            textColor: "black",
 | 
					            root: true,
 | 
				
			||||||
                            editable: false,
 | 
					          });
 | 
				
			||||||
                        }));
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
                    ctx.commit("calendarRanges/addExternals", inputs, {
 | 
					        })
 | 
				
			||||||
                        root: true,
 | 
					        .catch((e: TransportExceptionInterface) => {
 | 
				
			||||||
                    });
 | 
					          console.error(e);
 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .catch((e: TransportExceptionInterface) => {
 | 
					 | 
				
			||||||
                    console.error(e);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    return Promise.resolve(null);
 | 
					          return Promise.resolve(null);
 | 
				
			||||||
                });
 | 
					        });
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
} as Module<CalendarRemotesState, State>;
 | 
					} as Module<CalendarRemotesState, State>;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,77 +2,77 @@ import { State } from "./../index";
 | 
				
			|||||||
import { ActionContext } from "vuex";
 | 
					import { ActionContext } from "vuex";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface FullCalendarState {
 | 
					export interface FullCalendarState {
 | 
				
			||||||
    currentView: {
 | 
					  currentView: {
 | 
				
			||||||
        start: Date | null;
 | 
					    start: Date | null;
 | 
				
			||||||
        end: Date | null;
 | 
					    end: Date | null;
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
    key: number;
 | 
					  key: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Context = ActionContext<FullCalendarState, State>;
 | 
					type Context = ActionContext<FullCalendarState, State>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): FullCalendarState => ({
 | 
					  state: (): FullCalendarState => ({
 | 
				
			||||||
        currentView: {
 | 
					    currentView: {
 | 
				
			||||||
            start: null,
 | 
					      start: null,
 | 
				
			||||||
            end: null,
 | 
					      end: null,
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        key: 0,
 | 
					 | 
				
			||||||
    }),
 | 
					 | 
				
			||||||
    mutations: {
 | 
					 | 
				
			||||||
        setCurrentDatesView: function (
 | 
					 | 
				
			||||||
            state: FullCalendarState,
 | 
					 | 
				
			||||||
            payload: { start: Date; end: Date },
 | 
					 | 
				
			||||||
        ): void {
 | 
					 | 
				
			||||||
            state.currentView.start = payload.start;
 | 
					 | 
				
			||||||
            state.currentView.end = payload.end;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        increaseKey: function (state: FullCalendarState): void {
 | 
					 | 
				
			||||||
            state.key = state.key + 1;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					    key: 0,
 | 
				
			||||||
        setCurrentDatesView(
 | 
					  }),
 | 
				
			||||||
            ctx: Context,
 | 
					  mutations: {
 | 
				
			||||||
            { start, end }: { start: Date | null; end: Date | null },
 | 
					    setCurrentDatesView: function (
 | 
				
			||||||
        ): Promise<null> {
 | 
					      state: FullCalendarState,
 | 
				
			||||||
            console.log("dispatch setCurrentDatesView", { start, end });
 | 
					      payload: { start: Date; end: Date },
 | 
				
			||||||
 | 
					    ): void {
 | 
				
			||||||
            if (
 | 
					      state.currentView.start = payload.start;
 | 
				
			||||||
                ctx.state.currentView.start !== start ||
 | 
					      state.currentView.end = payload.end;
 | 
				
			||||||
                ctx.state.currentView.end !== end
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                ctx.commit("setCurrentDatesView", { start, end });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (start !== null && end !== null) {
 | 
					 | 
				
			||||||
                return Promise.all([
 | 
					 | 
				
			||||||
                    ctx
 | 
					 | 
				
			||||||
                        .dispatch(
 | 
					 | 
				
			||||||
                            "calendarRanges/fetchRanges",
 | 
					 | 
				
			||||||
                            { start, end },
 | 
					 | 
				
			||||||
                            { root: true },
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        .then((_) => Promise.resolve(null)),
 | 
					 | 
				
			||||||
                    ctx
 | 
					 | 
				
			||||||
                        .dispatch(
 | 
					 | 
				
			||||||
                            "calendarRemotes/fetchRemotes",
 | 
					 | 
				
			||||||
                            { start, end },
 | 
					 | 
				
			||||||
                            { root: true },
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        .then((_) => Promise.resolve(null)),
 | 
					 | 
				
			||||||
                    ctx
 | 
					 | 
				
			||||||
                        .dispatch(
 | 
					 | 
				
			||||||
                            "calendarLocals/fetchLocals",
 | 
					 | 
				
			||||||
                            { start, end },
 | 
					 | 
				
			||||||
                            { root: true },
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        .then((_) => Promise.resolve(null)),
 | 
					 | 
				
			||||||
                ]).then((_) => Promise.resolve(null));
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                return Promise.resolve(null);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    increaseKey: function (state: FullCalendarState): void {
 | 
				
			||||||
 | 
					      state.key = state.key + 1;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  actions: {
 | 
				
			||||||
 | 
					    setCurrentDatesView(
 | 
				
			||||||
 | 
					      ctx: Context,
 | 
				
			||||||
 | 
					      { start, end }: { start: Date | null; end: Date | null },
 | 
				
			||||||
 | 
					    ): Promise<null> {
 | 
				
			||||||
 | 
					      console.log("dispatch setCurrentDatesView", { start, end });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        ctx.state.currentView.start !== start ||
 | 
				
			||||||
 | 
					        ctx.state.currentView.end !== end
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        ctx.commit("setCurrentDatesView", { start, end });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (start !== null && end !== null) {
 | 
				
			||||||
 | 
					        return Promise.all([
 | 
				
			||||||
 | 
					          ctx
 | 
				
			||||||
 | 
					            .dispatch(
 | 
				
			||||||
 | 
					              "calendarRanges/fetchRanges",
 | 
				
			||||||
 | 
					              { start, end },
 | 
				
			||||||
 | 
					              { root: true },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .then((_) => Promise.resolve(null)),
 | 
				
			||||||
 | 
					          ctx
 | 
				
			||||||
 | 
					            .dispatch(
 | 
				
			||||||
 | 
					              "calendarRemotes/fetchRemotes",
 | 
				
			||||||
 | 
					              { start, end },
 | 
				
			||||||
 | 
					              { root: true },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .then((_) => Promise.resolve(null)),
 | 
				
			||||||
 | 
					          ctx
 | 
				
			||||||
 | 
					            .dispatch(
 | 
				
			||||||
 | 
					              "calendarLocals/fetchLocals",
 | 
				
			||||||
 | 
					              { start, end },
 | 
				
			||||||
 | 
					              { root: true },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .then((_) => Promise.resolve(null)),
 | 
				
			||||||
 | 
					        ]).then((_) => Promise.resolve(null));
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return Promise.resolve(null);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,61 +5,61 @@ import { getLocations } from "../../../../../../../ChillMainBundle/Resources/pub
 | 
				
			|||||||
import { whereami } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user";
 | 
					import { whereami } from "../../../../../../../ChillMainBundle/Resources/public/lib/api/user";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface LocationState {
 | 
					export interface LocationState {
 | 
				
			||||||
    locations: Location[];
 | 
					  locations: Location[];
 | 
				
			||||||
    locationPicked: Location | null;
 | 
					  locationPicked: Location | null;
 | 
				
			||||||
    currentLocation: Location | null;
 | 
					  currentLocation: Location | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): LocationState => {
 | 
					  state: (): LocationState => {
 | 
				
			||||||
        return {
 | 
					    return {
 | 
				
			||||||
            locations: [],
 | 
					      locations: [],
 | 
				
			||||||
            locationPicked: null,
 | 
					      locationPicked: null,
 | 
				
			||||||
            currentLocation: null,
 | 
					      currentLocation: null,
 | 
				
			||||||
        };
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  getters: {
 | 
				
			||||||
 | 
					    getLocationById:
 | 
				
			||||||
 | 
					      (state) =>
 | 
				
			||||||
 | 
					      (id: number): Location | undefined => {
 | 
				
			||||||
 | 
					        return state.locations.find((l) => l.id === id);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mutations: {
 | 
				
			||||||
 | 
					    setLocations(state, locations): void {
 | 
				
			||||||
 | 
					      state.locations = locations;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    getters: {
 | 
					    setLocationPicked(state, location: Location | null): void {
 | 
				
			||||||
        getLocationById:
 | 
					      if (null === location) {
 | 
				
			||||||
            (state) =>
 | 
					        state.locationPicked = null;
 | 
				
			||||||
            (id: number): Location | undefined => {
 | 
					        return;
 | 
				
			||||||
                return state.locations.find((l) => l.id === id);
 | 
					      }
 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    mutations: {
 | 
					 | 
				
			||||||
        setLocations(state, locations): void {
 | 
					 | 
				
			||||||
            state.locations = locations;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        setLocationPicked(state, location: Location | null): void {
 | 
					 | 
				
			||||||
            if (null === location) {
 | 
					 | 
				
			||||||
                state.locationPicked = null;
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            state.locationPicked =
 | 
					      state.locationPicked =
 | 
				
			||||||
                state.locations.find((l) => l.id === location.id) || null;
 | 
					        state.locations.find((l) => l.id === location.id) || null;
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        setCurrentLocation(state, location: Location | null): void {
 | 
					    setCurrentLocation(state, location: Location | null): void {
 | 
				
			||||||
            if (null === location) {
 | 
					      if (null === location) {
 | 
				
			||||||
                state.currentLocation = null;
 | 
					        state.currentLocation = null;
 | 
				
			||||||
                return;
 | 
					        return;
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            state.currentLocation =
 | 
					      state.currentLocation =
 | 
				
			||||||
                state.locations.find((l) => l.id === location.id) || null;
 | 
					        state.locations.find((l) => l.id === location.id) || null;
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    actions: {
 | 
					  },
 | 
				
			||||||
        getLocations(ctx): Promise<void> {
 | 
					  actions: {
 | 
				
			||||||
            return getLocations().then((locations) => {
 | 
					    getLocations(ctx): Promise<void> {
 | 
				
			||||||
                ctx.commit("setLocations", locations);
 | 
					      return getLocations().then((locations) => {
 | 
				
			||||||
                return Promise.resolve();
 | 
					        ctx.commit("setLocations", locations);
 | 
				
			||||||
            });
 | 
					        return Promise.resolve();
 | 
				
			||||||
        },
 | 
					      });
 | 
				
			||||||
        getCurrentLocation(ctx): Promise<void> {
 | 
					 | 
				
			||||||
            return whereami().then((location) => {
 | 
					 | 
				
			||||||
                ctx.commit("setCurrentLocation", location);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    getCurrentLocation(ctx): Promise<void> {
 | 
				
			||||||
 | 
					      return whereami().then((location) => {
 | 
				
			||||||
 | 
					        ctx.commit("setCurrentLocation", location);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
} as Module<LocationState, State>;
 | 
					} as Module<LocationState, State>;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,24 +3,24 @@ import { User } from "../../../../../../../ChillMainBundle/Resources/public/type
 | 
				
			|||||||
import { ActionContext } from "vuex";
 | 
					import { ActionContext } from "vuex";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface MeState {
 | 
					export interface MeState {
 | 
				
			||||||
    me: User | null;
 | 
					  me: User | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Context = ActionContext<MeState, State>;
 | 
					type Context = ActionContext<MeState, State>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    namespaced: true,
 | 
					  namespaced: true,
 | 
				
			||||||
    state: (): MeState => ({
 | 
					  state: (): MeState => ({
 | 
				
			||||||
        me: null,
 | 
					    me: null,
 | 
				
			||||||
    }),
 | 
					  }),
 | 
				
			||||||
    getters: {
 | 
					  getters: {
 | 
				
			||||||
        getMe: function (state: MeState): User | null {
 | 
					    getMe: function (state: MeState): User | null {
 | 
				
			||||||
            return state.me;
 | 
					      return state.me;
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mutations: {
 | 
					  },
 | 
				
			||||||
        setWhoAmi(state: MeState, me: User) {
 | 
					  mutations: {
 | 
				
			||||||
            state.me = me;
 | 
					    setWhoAmi(state: MeState, me: User) {
 | 
				
			||||||
        },
 | 
					      state.me = me;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,51 +1,51 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div>
 | 
					  <div>
 | 
				
			||||||
        <h2 class="chill-red">
 | 
					    <h2 class="chill-red">
 | 
				
			||||||
            {{ $t("choose_your_calendar_user") }}
 | 
					      {{ $t("choose_your_calendar_user") }}
 | 
				
			||||||
        </h2>
 | 
					    </h2>
 | 
				
			||||||
        <VueMultiselect
 | 
					    <VueMultiselect
 | 
				
			||||||
            name="field"
 | 
					      name="field"
 | 
				
			||||||
            id="calendarUserSelector"
 | 
					      id="calendarUserSelector"
 | 
				
			||||||
            v-model="value"
 | 
					      v-model="value"
 | 
				
			||||||
            track-by="id"
 | 
					      track-by="id"
 | 
				
			||||||
            label="value"
 | 
					      label="value"
 | 
				
			||||||
            :custom-label="transName"
 | 
					      :custom-label="transName"
 | 
				
			||||||
            :placeholder="$t('select_user')"
 | 
					      :placeholder="$t('select_user')"
 | 
				
			||||||
            :multiple="true"
 | 
					      :multiple="true"
 | 
				
			||||||
            :close-on-select="false"
 | 
					      :close-on-select="false"
 | 
				
			||||||
            :allow-empty="true"
 | 
					      :allow-empty="true"
 | 
				
			||||||
            :model-value="value"
 | 
					      :model-value="value"
 | 
				
			||||||
            :select-label="$t('multiselect.select_label')"
 | 
					      :select-label="$t('multiselect.select_label')"
 | 
				
			||||||
            :deselect-label="$t('multiselect.deselect_label')"
 | 
					      :deselect-label="$t('multiselect.deselect_label')"
 | 
				
			||||||
            :selected-label="$t('multiselect.selected_label')"
 | 
					      :selected-label="$t('multiselect.selected_label')"
 | 
				
			||||||
            @select="selectUsers"
 | 
					      @select="selectUsers"
 | 
				
			||||||
            @remove="unSelectUsers"
 | 
					      @remove="unSelectUsers"
 | 
				
			||||||
            @close="coloriseSelectedValues"
 | 
					      @close="coloriseSelectedValues"
 | 
				
			||||||
            :options="options"
 | 
					      :options="options"
 | 
				
			||||||
        />
 | 
					    />
 | 
				
			||||||
    </div>
 | 
					  </div>
 | 
				
			||||||
    <div class="form-check">
 | 
					  <div class="form-check">
 | 
				
			||||||
        <input
 | 
					    <input
 | 
				
			||||||
            type="checkbox"
 | 
					      type="checkbox"
 | 
				
			||||||
            id="myCalendar"
 | 
					      id="myCalendar"
 | 
				
			||||||
            class="form-check-input"
 | 
					      class="form-check-input"
 | 
				
			||||||
            v-model="showMyCalendarWidget"
 | 
					      v-model="showMyCalendarWidget"
 | 
				
			||||||
        />
 | 
					    />
 | 
				
			||||||
        <label class="form-check-label" for="myCalendar">{{
 | 
					    <label class="form-check-label" for="myCalendar">{{
 | 
				
			||||||
            $t("show_my_calendar")
 | 
					      $t("show_my_calendar")
 | 
				
			||||||
        }}</label>
 | 
					    }}</label>
 | 
				
			||||||
    </div>
 | 
					  </div>
 | 
				
			||||||
    <div class="form-check">
 | 
					  <div class="form-check">
 | 
				
			||||||
        <input
 | 
					    <input
 | 
				
			||||||
            type="checkbox"
 | 
					      type="checkbox"
 | 
				
			||||||
            id="weekends"
 | 
					      id="weekends"
 | 
				
			||||||
            class="form-check-input"
 | 
					      class="form-check-input"
 | 
				
			||||||
            @click="toggleWeekends"
 | 
					      @click="toggleWeekends"
 | 
				
			||||||
        />
 | 
					    />
 | 
				
			||||||
        <label class="form-check-label" for="weekends">{{
 | 
					    <label class="form-check-label" for="weekends">{{
 | 
				
			||||||
            $t("show_weekends")
 | 
					      $t("show_weekends")
 | 
				
			||||||
        }}</label>
 | 
					    }}</label>
 | 
				
			||||||
    </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { fetchCalendarRanges, fetchCalendar } from "../../_api/api";
 | 
					import { fetchCalendarRanges, fetchCalendar } from "../../_api/api";
 | 
				
			||||||
@@ -53,206 +53,183 @@ import VueMultiselect from "vue-multiselect";
 | 
				
			|||||||
import { whoami } from "ChillPersonAssets/vuejs/AccompanyingCourse/api";
 | 
					import { whoami } from "ChillPersonAssets/vuejs/AccompanyingCourse/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const COLORS = [
 | 
					const COLORS = [
 | 
				
			||||||
    /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
 | 
					  /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
 | 
				
			||||||
    "#8dd3c7",
 | 
					  "#8dd3c7",
 | 
				
			||||||
    "#ffffb3",
 | 
					  "#ffffb3",
 | 
				
			||||||
    "#bebada",
 | 
					  "#bebada",
 | 
				
			||||||
    "#fb8072",
 | 
					  "#fb8072",
 | 
				
			||||||
    "#80b1d3",
 | 
					  "#80b1d3",
 | 
				
			||||||
    "#fdb462",
 | 
					  "#fdb462",
 | 
				
			||||||
    "#b3de69",
 | 
					  "#b3de69",
 | 
				
			||||||
    "#fccde5",
 | 
					  "#fccde5",
 | 
				
			||||||
    "#d9d9d9",
 | 
					  "#d9d9d9",
 | 
				
			||||||
    "#bc80bd",
 | 
					  "#bc80bd",
 | 
				
			||||||
    "#ccebc5",
 | 
					  "#ccebc5",
 | 
				
			||||||
    "#ffed6f",
 | 
					  "#ffed6f",
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "CalendarUserSelector",
 | 
					  name: "CalendarUserSelector",
 | 
				
			||||||
    components: { VueMultiselect },
 | 
					  components: { VueMultiselect },
 | 
				
			||||||
    props: [
 | 
					  props: [
 | 
				
			||||||
        "users",
 | 
					    "users",
 | 
				
			||||||
        "updateEventsSource",
 | 
					    "updateEventsSource",
 | 
				
			||||||
        "calendarEvents",
 | 
					    "calendarEvents",
 | 
				
			||||||
        "showMyCalendar",
 | 
					    "showMyCalendar",
 | 
				
			||||||
        "toggleMyCalendar",
 | 
					    "toggleMyCalendar",
 | 
				
			||||||
        "toggleWeekends",
 | 
					    "toggleWeekends",
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
    data() {
 | 
					  data() {
 | 
				
			||||||
        return {
 | 
					    return {
 | 
				
			||||||
            errorMsg: [],
 | 
					      errorMsg: [],
 | 
				
			||||||
            value: [],
 | 
					      value: [],
 | 
				
			||||||
            options: [],
 | 
					      options: [],
 | 
				
			||||||
        };
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    showMyCalendarWidget: {
 | 
				
			||||||
 | 
					      set(value) {
 | 
				
			||||||
 | 
					        this.toggleMyCalendar(value);
 | 
				
			||||||
 | 
					        this.updateEventsSource();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      get() {
 | 
				
			||||||
 | 
					        return this.showMyCalendar;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					  },
 | 
				
			||||||
        showMyCalendarWidget: {
 | 
					  methods: {
 | 
				
			||||||
            set(value) {
 | 
					    init() {
 | 
				
			||||||
                this.toggleMyCalendar(value);
 | 
					      this.fetchData();
 | 
				
			||||||
                this.updateEventsSource();
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            get() {
 | 
					 | 
				
			||||||
                return this.showMyCalendar;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					    fetchData() {
 | 
				
			||||||
        init() {
 | 
					      fetchCalendarRanges()
 | 
				
			||||||
            this.fetchData();
 | 
					        .then(
 | 
				
			||||||
        },
 | 
					          (calendarRanges) =>
 | 
				
			||||||
        fetchData() {
 | 
					            new Promise((resolve, reject) => {
 | 
				
			||||||
            fetchCalendarRanges()
 | 
					              let results = calendarRanges.results;
 | 
				
			||||||
                .then(
 | 
					 | 
				
			||||||
                    (calendarRanges) =>
 | 
					 | 
				
			||||||
                        new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
                            let results = calendarRanges.results;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            let users = [];
 | 
					              let users = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            results.forEach((i) => {
 | 
					              results.forEach((i) => {
 | 
				
			||||||
                                if (!users.some((j) => i.user.id === j.id)) {
 | 
					                if (!users.some((j) => i.user.id === j.id)) {
 | 
				
			||||||
                                    let ratio = Math.floor(
 | 
					                  let ratio = Math.floor(users.length / COLORS.length);
 | 
				
			||||||
                                        users.length / COLORS.length,
 | 
					                  let colorIndex = users.length - ratio * COLORS.length;
 | 
				
			||||||
                                    );
 | 
					                  users.push({
 | 
				
			||||||
                                    let colorIndex =
 | 
					                    id: i.user.id,
 | 
				
			||||||
                                        users.length - ratio * COLORS.length;
 | 
					                    username: i.user.username,
 | 
				
			||||||
                                    users.push({
 | 
					                    color: COLORS[colorIndex],
 | 
				
			||||||
                                        id: i.user.id,
 | 
					                  });
 | 
				
			||||||
                                        username: i.user.username,
 | 
					 | 
				
			||||||
                                        color: COLORS[colorIndex],
 | 
					 | 
				
			||||||
                                    });
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            let calendarEvents = [];
 | 
					 | 
				
			||||||
                            users.forEach((u) => {
 | 
					 | 
				
			||||||
                                let arr = results
 | 
					 | 
				
			||||||
                                    .filter((i) => i.user.id === u.id)
 | 
					 | 
				
			||||||
                                    .map((i) => ({
 | 
					 | 
				
			||||||
                                        start: i.startDate.datetime,
 | 
					 | 
				
			||||||
                                        end: i.endDate.datetime,
 | 
					 | 
				
			||||||
                                        calendarRangeId: i.id,
 | 
					 | 
				
			||||||
                                        sourceColor: u.color,
 | 
					 | 
				
			||||||
                                        //display: 'background' // can be an option for the disponibility
 | 
					 | 
				
			||||||
                                    }));
 | 
					 | 
				
			||||||
                                calendarEvents.push({
 | 
					 | 
				
			||||||
                                    events: arr,
 | 
					 | 
				
			||||||
                                    color: u.color,
 | 
					 | 
				
			||||||
                                    textColor: "#444444",
 | 
					 | 
				
			||||||
                                    editable: false,
 | 
					 | 
				
			||||||
                                    id: u.id,
 | 
					 | 
				
			||||||
                                });
 | 
					 | 
				
			||||||
                            });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            this.users.loaded = users;
 | 
					 | 
				
			||||||
                            this.options = users;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            this.calendarEvents.loaded = calendarEvents;
 | 
					 | 
				
			||||||
                            whoami().then(
 | 
					 | 
				
			||||||
                                (me) =>
 | 
					 | 
				
			||||||
                                    new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
                                        this.users.logged = me;
 | 
					 | 
				
			||||||
                                        let currentUser = users.find(
 | 
					 | 
				
			||||||
                                            (u) => u.id === me.id,
 | 
					 | 
				
			||||||
                                        );
 | 
					 | 
				
			||||||
                                        this.value = currentUser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        fetchCalendar(currentUser.id).then(
 | 
					 | 
				
			||||||
                                            (calendar) =>
 | 
					 | 
				
			||||||
                                                new Promise(
 | 
					 | 
				
			||||||
                                                    (resolve, reject) => {
 | 
					 | 
				
			||||||
                                                        let results =
 | 
					 | 
				
			||||||
                                                            calendar.results;
 | 
					 | 
				
			||||||
                                                        let events =
 | 
					 | 
				
			||||||
                                                            results.map(
 | 
					 | 
				
			||||||
                                                                (i) => ({
 | 
					 | 
				
			||||||
                                                                    start: i
 | 
					 | 
				
			||||||
                                                                        .startDate
 | 
					 | 
				
			||||||
                                                                        .datetime,
 | 
					 | 
				
			||||||
                                                                    end: i
 | 
					 | 
				
			||||||
                                                                        .endDate
 | 
					 | 
				
			||||||
                                                                        .datetime,
 | 
					 | 
				
			||||||
                                                                }),
 | 
					 | 
				
			||||||
                                                            );
 | 
					 | 
				
			||||||
                                                        let calendarEventsCurrentUser =
 | 
					 | 
				
			||||||
                                                            {
 | 
					 | 
				
			||||||
                                                                events: events,
 | 
					 | 
				
			||||||
                                                                color: "darkblue",
 | 
					 | 
				
			||||||
                                                                id: 1000,
 | 
					 | 
				
			||||||
                                                                editable: false,
 | 
					 | 
				
			||||||
                                                            };
 | 
					 | 
				
			||||||
                                                        this.calendarEvents.user =
 | 
					 | 
				
			||||||
                                                            calendarEventsCurrentUser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                                        this.selectUsers(
 | 
					 | 
				
			||||||
                                                            currentUser,
 | 
					 | 
				
			||||||
                                                        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                                        resolve();
 | 
					 | 
				
			||||||
                                                    },
 | 
					 | 
				
			||||||
                                                ),
 | 
					 | 
				
			||||||
                                        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                        resolve();
 | 
					 | 
				
			||||||
                                    }),
 | 
					 | 
				
			||||||
                            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            resolve();
 | 
					 | 
				
			||||||
                        }),
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                .catch((error) => {
 | 
					 | 
				
			||||||
                    this.errorMsg.push(error.message);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        transName(value) {
 | 
					 | 
				
			||||||
            return `${value.username}`;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        coloriseSelectedValues() {
 | 
					 | 
				
			||||||
            let tags = document.querySelectorAll(
 | 
					 | 
				
			||||||
                "div.multiselect__tags-wrap",
 | 
					 | 
				
			||||||
            )[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (tags.hasChildNodes()) {
 | 
					 | 
				
			||||||
                let children = tags.childNodes;
 | 
					 | 
				
			||||||
                for (let i = 0; i < children.length; i++) {
 | 
					 | 
				
			||||||
                    let child = children[i];
 | 
					 | 
				
			||||||
                    if (child.nodeType === Node.ELEMENT_NODE) {
 | 
					 | 
				
			||||||
                        this.users.selected.forEach((u) => {
 | 
					 | 
				
			||||||
                            if (child.hasChildNodes()) {
 | 
					 | 
				
			||||||
                                if (child.firstChild.innerText == u.username) {
 | 
					 | 
				
			||||||
                                    child.style.background = u.color;
 | 
					 | 
				
			||||||
                                    child.firstChild.style.color = "#444444";
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					              });
 | 
				
			||||||
        },
 | 
					
 | 
				
			||||||
        selectEvents() {
 | 
					              let calendarEvents = [];
 | 
				
			||||||
            let selectedUsersId = this.users.selected.map((a) => a.id);
 | 
					              users.forEach((u) => {
 | 
				
			||||||
            this.calendarEvents.selected = this.calendarEvents.loaded.filter(
 | 
					                let arr = results
 | 
				
			||||||
                (a) => selectedUsersId.includes(a.id),
 | 
					                  .filter((i) => i.user.id === u.id)
 | 
				
			||||||
            );
 | 
					                  .map((i) => ({
 | 
				
			||||||
        },
 | 
					                    start: i.startDate.datetime,
 | 
				
			||||||
        selectUsers(value) {
 | 
					                    end: i.endDate.datetime,
 | 
				
			||||||
            this.users.selected.push(value);
 | 
					                    calendarRangeId: i.id,
 | 
				
			||||||
            this.coloriseSelectedValues();
 | 
					                    sourceColor: u.color,
 | 
				
			||||||
            this.selectEvents();
 | 
					                    //display: 'background' // can be an option for the disponibility
 | 
				
			||||||
            this.updateEventsSource();
 | 
					                  }));
 | 
				
			||||||
        },
 | 
					                calendarEvents.push({
 | 
				
			||||||
        unSelectUsers(value) {
 | 
					                  events: arr,
 | 
				
			||||||
            this.users.selected = this.users.selected.filter(
 | 
					                  color: u.color,
 | 
				
			||||||
                (a) => a.id != value.id,
 | 
					                  textColor: "#444444",
 | 
				
			||||||
            );
 | 
					                  editable: false,
 | 
				
			||||||
            this.selectEvents();
 | 
					                  id: u.id,
 | 
				
			||||||
            this.updateEventsSource();
 | 
					                });
 | 
				
			||||||
        },
 | 
					              });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              this.users.loaded = users;
 | 
				
			||||||
 | 
					              this.options = users;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              this.calendarEvents.loaded = calendarEvents;
 | 
				
			||||||
 | 
					              whoami().then(
 | 
				
			||||||
 | 
					                (me) =>
 | 
				
			||||||
 | 
					                  new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					                    this.users.logged = me;
 | 
				
			||||||
 | 
					                    let currentUser = users.find((u) => u.id === me.id);
 | 
				
			||||||
 | 
					                    this.value = currentUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    fetchCalendar(currentUser.id).then(
 | 
				
			||||||
 | 
					                      (calendar) =>
 | 
				
			||||||
 | 
					                        new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					                          let results = calendar.results;
 | 
				
			||||||
 | 
					                          let events = results.map((i) => ({
 | 
				
			||||||
 | 
					                            start: i.startDate.datetime,
 | 
				
			||||||
 | 
					                            end: i.endDate.datetime,
 | 
				
			||||||
 | 
					                          }));
 | 
				
			||||||
 | 
					                          let calendarEventsCurrentUser = {
 | 
				
			||||||
 | 
					                            events: events,
 | 
				
			||||||
 | 
					                            color: "darkblue",
 | 
				
			||||||
 | 
					                            id: 1000,
 | 
				
			||||||
 | 
					                            editable: false,
 | 
				
			||||||
 | 
					                          };
 | 
				
			||||||
 | 
					                          this.calendarEvents.user = calendarEventsCurrentUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                          this.selectUsers(currentUser);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                          resolve();
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    resolve();
 | 
				
			||||||
 | 
					                  }),
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              resolve();
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .catch((error) => {
 | 
				
			||||||
 | 
					          this.errorMsg.push(error.message);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mounted() {
 | 
					    transName(value) {
 | 
				
			||||||
        this.init();
 | 
					      return `${value.username}`;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    coloriseSelectedValues() {
 | 
				
			||||||
 | 
					      let tags = document.querySelectorAll("div.multiselect__tags-wrap")[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (tags.hasChildNodes()) {
 | 
				
			||||||
 | 
					        let children = tags.childNodes;
 | 
				
			||||||
 | 
					        for (let i = 0; i < children.length; i++) {
 | 
				
			||||||
 | 
					          let child = children[i];
 | 
				
			||||||
 | 
					          if (child.nodeType === Node.ELEMENT_NODE) {
 | 
				
			||||||
 | 
					            this.users.selected.forEach((u) => {
 | 
				
			||||||
 | 
					              if (child.hasChildNodes()) {
 | 
				
			||||||
 | 
					                if (child.firstChild.innerText == u.username) {
 | 
				
			||||||
 | 
					                  child.style.background = u.color;
 | 
				
			||||||
 | 
					                  child.firstChild.style.color = "#444444";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    selectEvents() {
 | 
				
			||||||
 | 
					      let selectedUsersId = this.users.selected.map((a) => a.id);
 | 
				
			||||||
 | 
					      this.calendarEvents.selected = this.calendarEvents.loaded.filter((a) =>
 | 
				
			||||||
 | 
					        selectedUsersId.includes(a.id),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    selectUsers(value) {
 | 
				
			||||||
 | 
					      this.users.selected.push(value);
 | 
				
			||||||
 | 
					      this.coloriseSelectedValues();
 | 
				
			||||||
 | 
					      this.selectEvents();
 | 
				
			||||||
 | 
					      this.updateEventsSource();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    unSelectUsers(value) {
 | 
				
			||||||
 | 
					      this.users.selected = this.users.selected.filter((a) => a.id != value.id);
 | 
				
			||||||
 | 
					      this.selectEvents();
 | 
				
			||||||
 | 
					      this.updateEventsSource();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    this.init();
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,59 +1,54 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div>
 | 
					  <div>
 | 
				
			||||||
        <template v-if="templates.length > 0">
 | 
					    <template v-if="templates.length > 0">
 | 
				
			||||||
            <slot name="title">
 | 
					      <slot name="title">
 | 
				
			||||||
                <h2>{{ $t("generate_document") }}</h2>
 | 
					        <h2>{{ $t("generate_document") }}</h2>
 | 
				
			||||||
            </slot>
 | 
					      </slot>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="container">
 | 
					      <div class="container">
 | 
				
			||||||
                <div class="row">
 | 
					        <div class="row">
 | 
				
			||||||
                    <div class="col-md-4">
 | 
					          <div class="col-md-4">
 | 
				
			||||||
                        <slot name="label">
 | 
					            <slot name="label">
 | 
				
			||||||
                            <label>{{ $t("select_a_template") }}</label>
 | 
					              <label>{{ $t("select_a_template") }}</label>
 | 
				
			||||||
                        </slot>
 | 
					            </slot>
 | 
				
			||||||
                    </div>
 | 
					          </div>
 | 
				
			||||||
                    <div class="col-md-8">
 | 
					          <div class="col-md-8">
 | 
				
			||||||
                        <div class="input-group mb-3">
 | 
					            <div class="input-group mb-3">
 | 
				
			||||||
                            <select class="form-select" v-model="template">
 | 
					              <select class="form-select" v-model="template">
 | 
				
			||||||
                                <option disabled selected value="">
 | 
					                <option disabled selected value="">
 | 
				
			||||||
                                    {{ $t("choose_a_template") }}
 | 
					                  {{ $t("choose_a_template") }}
 | 
				
			||||||
                                </option>
 | 
					                </option>
 | 
				
			||||||
                                <template v-for="t in templates" :key="t.id">
 | 
					                <template v-for="t in templates" :key="t.id">
 | 
				
			||||||
                                    <option :value="t.id">
 | 
					                  <option :value="t.id">
 | 
				
			||||||
                                        {{
 | 
					                    {{ localizeString(t.name) || "Aucun nom défini" }}
 | 
				
			||||||
                                            localizeString(t.name) ||
 | 
					                  </option>
 | 
				
			||||||
                                            "Aucun nom défini"
 | 
					                </template>
 | 
				
			||||||
                                        }}
 | 
					              </select>
 | 
				
			||||||
                                    </option>
 | 
					              <a
 | 
				
			||||||
                                </template>
 | 
					                v-if="canGenerate"
 | 
				
			||||||
                            </select>
 | 
					                class="btn btn-update btn-sm change-icon"
 | 
				
			||||||
                            <a
 | 
					                :href="buildUrlGenerate"
 | 
				
			||||||
                                v-if="canGenerate"
 | 
					                @click.prevent="clickGenerate($event, buildUrlGenerate)"
 | 
				
			||||||
                                class="btn btn-update btn-sm change-icon"
 | 
					                ><i class="fa fa-fw fa-cog"
 | 
				
			||||||
                                :href="buildUrlGenerate"
 | 
					              /></a>
 | 
				
			||||||
                                @click.prevent="
 | 
					              <a
 | 
				
			||||||
                                    clickGenerate($event, buildUrlGenerate)
 | 
					                v-else
 | 
				
			||||||
                                "
 | 
					                class="btn btn-update btn-sm change-icon"
 | 
				
			||||||
                                ><i class="fa fa-fw fa-cog"
 | 
					                href="#"
 | 
				
			||||||
                            /></a>
 | 
					                disabled
 | 
				
			||||||
                            <a
 | 
					                ><i class="fa fa-fw fa-cog"
 | 
				
			||||||
                                v-else
 | 
					              /></a>
 | 
				
			||||||
                                class="btn btn-update btn-sm change-icon"
 | 
					 | 
				
			||||||
                                href="#"
 | 
					 | 
				
			||||||
                                disabled
 | 
					 | 
				
			||||||
                                ><i class="fa fa-fw fa-cog"
 | 
					 | 
				
			||||||
                            /></a>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div class="row" v-if="hasDescription">
 | 
					 | 
				
			||||||
                    <div class="col-md-8 align-self-end">
 | 
					 | 
				
			||||||
                        <p>{{ getDescription }}</p>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </template>
 | 
					          </div>
 | 
				
			||||||
    </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row" v-if="hasDescription">
 | 
				
			||||||
 | 
					          <div class="col-md-8 align-self-end">
 | 
				
			||||||
 | 
					            <p>{{ getDescription }}</p>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </template>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
@@ -61,83 +56,83 @@ import { buildLink } from "ChillDocGeneratorAssets/lib/document-generator";
 | 
				
			|||||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
					import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "PickTemplate",
 | 
					  name: "PickTemplate",
 | 
				
			||||||
    props: {
 | 
					  props: {
 | 
				
			||||||
        entityId: [String, Number],
 | 
					    entityId: [String, Number],
 | 
				
			||||||
        entityClass: {
 | 
					    entityClass: {
 | 
				
			||||||
            type: String,
 | 
					      type: String,
 | 
				
			||||||
            required: false,
 | 
					      required: false,
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        templates: {
 | 
					 | 
				
			||||||
            type: Array,
 | 
					 | 
				
			||||||
            required: true,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        preventDefaultMoveToGenerate: {
 | 
					 | 
				
			||||||
            type: Boolean,
 | 
					 | 
				
			||||||
            required: false,
 | 
					 | 
				
			||||||
            default: false,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    emits: ["goToGenerateDocument"],
 | 
					    templates: {
 | 
				
			||||||
    data() {
 | 
					      type: Array,
 | 
				
			||||||
        return {
 | 
					      required: true,
 | 
				
			||||||
            template: null,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    computed: {
 | 
					    preventDefaultMoveToGenerate: {
 | 
				
			||||||
        canGenerate() {
 | 
					      type: Boolean,
 | 
				
			||||||
            return this.template != null;
 | 
					      required: false,
 | 
				
			||||||
        },
 | 
					      default: false,
 | 
				
			||||||
        hasDescription() {
 | 
					    },
 | 
				
			||||||
            if (this.template == null) {
 | 
					  },
 | 
				
			||||||
                return false;
 | 
					  emits: ["goToGenerateDocument"],
 | 
				
			||||||
            }
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      template: null,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    canGenerate() {
 | 
				
			||||||
 | 
					      return this.template != null;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    hasDescription() {
 | 
				
			||||||
 | 
					      if (this.template == null) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return true;
 | 
					      return true;
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        getDescription() {
 | 
					    getDescription() {
 | 
				
			||||||
            if (null === this.template) {
 | 
					      if (null === this.template) {
 | 
				
			||||||
                return "";
 | 
					        return "";
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            let desc = this.templates.find((t) => t.id === this.template);
 | 
					      let desc = this.templates.find((t) => t.id === this.template);
 | 
				
			||||||
            if (null === desc) {
 | 
					      if (null === desc) {
 | 
				
			||||||
                return "";
 | 
					        return "";
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
            return desc.description || "";
 | 
					      return desc.description || "";
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        buildUrlGenerate() {
 | 
					    buildUrlGenerate() {
 | 
				
			||||||
            if (null === this.template) {
 | 
					      if (null === this.template) {
 | 
				
			||||||
                return "#";
 | 
					        return "#";
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return buildLink(this.template, this.entityId, this.entityClass);
 | 
					      return buildLink(this.template, this.entityId, this.entityClass);
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    methods: {
 | 
					  },
 | 
				
			||||||
        localizeString(str) {
 | 
					  methods: {
 | 
				
			||||||
            return localizeString(str);
 | 
					    localizeString(str) {
 | 
				
			||||||
        },
 | 
					      return localizeString(str);
 | 
				
			||||||
        clickGenerate(event, link) {
 | 
					    },
 | 
				
			||||||
            if (!this.preventDefaultMoveToGenerate) {
 | 
					    clickGenerate(event, link) {
 | 
				
			||||||
                window.location.assign(link);
 | 
					      if (!this.preventDefaultMoveToGenerate) {
 | 
				
			||||||
            }
 | 
					        window.location.assign(link);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.$emit("goToGenerateDocument", {
 | 
					      this.$emit("goToGenerateDocument", {
 | 
				
			||||||
                event,
 | 
					        event,
 | 
				
			||||||
                link,
 | 
					        link,
 | 
				
			||||||
                template: this.template,
 | 
					        template: this.template,
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    i18n: {
 | 
					  },
 | 
				
			||||||
        messages: {
 | 
					  i18n: {
 | 
				
			||||||
            fr: {
 | 
					    messages: {
 | 
				
			||||||
                generate_document: "Générer un document",
 | 
					      fr: {
 | 
				
			||||||
                select_a_template: "Choisir un modèle",
 | 
					        generate_document: "Générer un document",
 | 
				
			||||||
                choose_a_template: "Choisir",
 | 
					        select_a_template: "Choisir un modèle",
 | 
				
			||||||
            },
 | 
					        choose_a_template: "Choisir",
 | 
				
			||||||
        },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,7 @@ class StoredObject implements Document, TrackCreationInterface
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var Collection<int, StoredObjectVersion>&Selectable<int, StoredObjectVersion>
 | 
					     * @var Collection<int, StoredObjectVersion>&Selectable<int, StoredObjectVersion>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    #[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true)]
 | 
					    #[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true, fetch: 'EAGER')]
 | 
				
			||||||
    private Collection&Selectable $versions;
 | 
					    private Collection&Selectable $versions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,20 +6,20 @@ const algo = "AES-CBC";
 | 
				
			|||||||
const URL_POST = "/asyncupload/temp_url/generate/post";
 | 
					const URL_POST = "/asyncupload/temp_url/generate/post";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const keyDefinition = {
 | 
					const keyDefinition = {
 | 
				
			||||||
    name: algo,
 | 
					  name: algo,
 | 
				
			||||||
    length: 256,
 | 
					  length: 256,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createFilename = (): string => {
 | 
					const createFilename = (): string => {
 | 
				
			||||||
    let text = "";
 | 
					  let text = "";
 | 
				
			||||||
    const possible =
 | 
					  const possible =
 | 
				
			||||||
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
					    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let i = 0; i < 7; i++) {
 | 
					  for (let i = 0; i < 7; i++) {
 | 
				
			||||||
        text += possible.charAt(Math.floor(Math.random() * possible.length));
 | 
					    text += possible.charAt(Math.floor(Math.random() * possible.length));
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return text;
 | 
					  return text;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -30,59 +30,59 @@ const createFilename = (): string => {
 | 
				
			|||||||
 * @returns {Promise<StoredObject>} A Promise that resolves to the newly created StoredObject.
 | 
					 * @returns {Promise<StoredObject>} A Promise that resolves to the newly created StoredObject.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const fetchNewStoredObject = async (): Promise<StoredObject> => {
 | 
					export const fetchNewStoredObject = async (): Promise<StoredObject> => {
 | 
				
			||||||
    return makeFetch("POST", "/api/1.0/doc-store/stored-object/create", null);
 | 
					  return makeFetch("POST", "/api/1.0/doc-store/stored-object/create", null);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const uploadVersion = async (
 | 
					export const uploadVersion = async (
 | 
				
			||||||
    uploadFile: ArrayBuffer,
 | 
					  uploadFile: ArrayBuffer,
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
): Promise<string> => {
 | 
					): Promise<string> => {
 | 
				
			||||||
    const params = new URLSearchParams();
 | 
					  const params = new URLSearchParams();
 | 
				
			||||||
    params.append("expires_delay", "180");
 | 
					  params.append("expires_delay", "180");
 | 
				
			||||||
    params.append("submit_delay", "180");
 | 
					  params.append("submit_delay", "180");
 | 
				
			||||||
    const asyncData: PostStoreObjectSignature = await makeFetch(
 | 
					  const asyncData: PostStoreObjectSignature = await makeFetch(
 | 
				
			||||||
        "GET",
 | 
					    "GET",
 | 
				
			||||||
        `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/post` +
 | 
					    `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/post` +
 | 
				
			||||||
            "?" +
 | 
					      "?" +
 | 
				
			||||||
            params.toString(),
 | 
					      params.toString(),
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
    const suffix = createFilename();
 | 
					  const suffix = createFilename();
 | 
				
			||||||
    const filename = asyncData.prefix + suffix;
 | 
					  const filename = asyncData.prefix + suffix;
 | 
				
			||||||
    const formData = new FormData();
 | 
					  const formData = new FormData();
 | 
				
			||||||
    formData.append("redirect", asyncData.redirect);
 | 
					  formData.append("redirect", asyncData.redirect);
 | 
				
			||||||
    formData.append("max_file_size", asyncData.max_file_size.toString());
 | 
					  formData.append("max_file_size", asyncData.max_file_size.toString());
 | 
				
			||||||
    formData.append("max_file_count", asyncData.max_file_count.toString());
 | 
					  formData.append("max_file_count", asyncData.max_file_count.toString());
 | 
				
			||||||
    formData.append("expires", asyncData.expires.toString());
 | 
					  formData.append("expires", asyncData.expires.toString());
 | 
				
			||||||
    formData.append("signature", asyncData.signature);
 | 
					  formData.append("signature", asyncData.signature);
 | 
				
			||||||
    formData.append(filename, new Blob([uploadFile]), suffix);
 | 
					  formData.append(filename, new Blob([uploadFile]), suffix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const response = await window.fetch(asyncData.url, {
 | 
					  const response = await window.fetch(asyncData.url, {
 | 
				
			||||||
        method: "POST",
 | 
					    method: "POST",
 | 
				
			||||||
        body: formData,
 | 
					    body: formData,
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!response.ok) {
 | 
					  if (!response.ok) {
 | 
				
			||||||
        console.error("Error while sending file to store", response);
 | 
					    console.error("Error while sending file to store", response);
 | 
				
			||||||
        throw new Error(response.statusText);
 | 
					    throw new Error(response.statusText);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Promise.resolve(filename);
 | 
					  return Promise.resolve(filename);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const encryptFile = async (
 | 
					export const encryptFile = async (
 | 
				
			||||||
    originalFile: ArrayBuffer,
 | 
					  originalFile: ArrayBuffer,
 | 
				
			||||||
): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
 | 
					): Promise<[ArrayBuffer, Uint8Array, JsonWebKey]> => {
 | 
				
			||||||
    const iv = crypto.getRandomValues(new Uint8Array(16));
 | 
					  const iv = crypto.getRandomValues(new Uint8Array(16));
 | 
				
			||||||
    const key = await window.crypto.subtle.generateKey(keyDefinition, true, [
 | 
					  const key = await window.crypto.subtle.generateKey(keyDefinition, true, [
 | 
				
			||||||
        "encrypt",
 | 
					    "encrypt",
 | 
				
			||||||
        "decrypt",
 | 
					    "decrypt",
 | 
				
			||||||
    ]);
 | 
					  ]);
 | 
				
			||||||
    const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
 | 
					  const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
 | 
				
			||||||
    const encrypted = await window.crypto.subtle.encrypt(
 | 
					  const encrypted = await window.crypto.subtle.encrypt(
 | 
				
			||||||
        { name: algo, iv: iv },
 | 
					    { name: algo, iv: iv },
 | 
				
			||||||
        key,
 | 
					    key,
 | 
				
			||||||
        originalFile,
 | 
					    originalFile,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Promise.resolve([encrypted, iv, exportedKey]);
 | 
					  return Promise.resolve([encrypted, iv, exportedKey]);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,9 @@ import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
 | 
				
			|||||||
import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc";
 | 
					import { GenericDocForAccompanyingPeriod } from "ChillDocStoreAssets/types/generic_doc";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetch_generic_docs_by_accompanying_period(
 | 
					export function fetch_generic_docs_by_accompanying_period(
 | 
				
			||||||
    periodId: number,
 | 
					  periodId: number,
 | 
				
			||||||
): Promise<GenericDocForAccompanyingPeriod[]> {
 | 
					): Promise<GenericDocForAccompanyingPeriod[]> {
 | 
				
			||||||
    return fetchResults(
 | 
					  return fetchResults(
 | 
				
			||||||
        `/api/1.0/doc-store/generic-doc/by-period/${periodId}/index`,
 | 
					    `/api/1.0/doc-store/generic-doc/by-period/${periodId}/index`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,117 +6,116 @@ import { _createI18n } from "../../../../../ChillMainBundle/Resources/public/vue
 | 
				
			|||||||
const i18n = _createI18n({});
 | 
					const i18n = _createI18n({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const startApp = (
 | 
					const startApp = (
 | 
				
			||||||
    divElement: HTMLDivElement,
 | 
					  divElement: HTMLDivElement,
 | 
				
			||||||
    collectionEntry: null | HTMLLIElement,
 | 
					  collectionEntry: null | HTMLLIElement,
 | 
				
			||||||
): void => {
 | 
					): void => {
 | 
				
			||||||
    console.log("app started", divElement);
 | 
					  console.log("app started", divElement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const inputTitle = collectionEntry?.querySelector("input[type='text']");
 | 
					  const inputTitle = collectionEntry?.querySelector("input[type='text']");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const input_stored_object: HTMLInputElement | null =
 | 
					  const input_stored_object: HTMLInputElement | null = divElement.querySelector(
 | 
				
			||||||
        divElement.querySelector("input[data-stored-object]");
 | 
					    "input[data-stored-object]",
 | 
				
			||||||
    if (null === input_stored_object) {
 | 
					  );
 | 
				
			||||||
        throw new Error("input to stored object not found");
 | 
					  if (null === input_stored_object) {
 | 
				
			||||||
    }
 | 
					    throw new Error("input to stored object not found");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let existingDoc: StoredObject | null = null;
 | 
					  let existingDoc: StoredObject | null = null;
 | 
				
			||||||
    if (input_stored_object.value !== "") {
 | 
					  if (input_stored_object.value !== "") {
 | 
				
			||||||
        existingDoc = JSON.parse(input_stored_object.value);
 | 
					    existingDoc = JSON.parse(input_stored_object.value);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    const app_container = document.createElement("div");
 | 
					  const app_container = document.createElement("div");
 | 
				
			||||||
    divElement.appendChild(app_container);
 | 
					  divElement.appendChild(app_container);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const app = createApp({
 | 
					  const app = createApp({
 | 
				
			||||||
        template:
 | 
					    template:
 | 
				
			||||||
            '<drop-file-widget :existingDoc="this.$data.existingDoc" :allowRemove="true" @addDocument="this.addDocument" @removeDocument="removeDocument"></drop-file-widget>',
 | 
					      '<drop-file-widget :existingDoc="this.$data.existingDoc" :allowRemove="true" @addDocument="this.addDocument" @removeDocument="removeDocument"></drop-file-widget>',
 | 
				
			||||||
        data() {
 | 
					    data() {
 | 
				
			||||||
            return {
 | 
					      return {
 | 
				
			||||||
                existingDoc: existingDoc,
 | 
					        existingDoc: existingDoc,
 | 
				
			||||||
                inputTitle: inputTitle,
 | 
					        inputTitle: inputTitle,
 | 
				
			||||||
            };
 | 
					      };
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        components: {
 | 
					    components: {
 | 
				
			||||||
            DropFileWidget,
 | 
					      DropFileWidget,
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
        methods: {
 | 
					    methods: {
 | 
				
			||||||
            addDocument: function ({
 | 
					      addDocument: function ({
 | 
				
			||||||
                stored_object,
 | 
					        stored_object,
 | 
				
			||||||
                stored_object_version,
 | 
					        stored_object_version,
 | 
				
			||||||
                file_name,
 | 
					        file_name,
 | 
				
			||||||
            }: {
 | 
					      }: {
 | 
				
			||||||
                stored_object: StoredObject;
 | 
					        stored_object: StoredObject;
 | 
				
			||||||
                stored_object_version: StoredObjectVersion;
 | 
					        stored_object_version: StoredObjectVersion;
 | 
				
			||||||
                file_name: string;
 | 
					        file_name: string;
 | 
				
			||||||
            }): void {
 | 
					      }): void {
 | 
				
			||||||
                stored_object.title = file_name;
 | 
					        stored_object.title = file_name;
 | 
				
			||||||
                console.log("object added", stored_object);
 | 
					        console.log("object added", stored_object);
 | 
				
			||||||
                console.log("version added", stored_object_version);
 | 
					        console.log("version added", stored_object_version);
 | 
				
			||||||
                this.$data.existingDoc = stored_object;
 | 
					        this.$data.existingDoc = stored_object;
 | 
				
			||||||
                this.$data.existingDoc.currentVersion = stored_object_version;
 | 
					        this.$data.existingDoc.currentVersion = stored_object_version;
 | 
				
			||||||
                input_stored_object.value = JSON.stringify(
 | 
					        input_stored_object.value = JSON.stringify(this.$data.existingDoc);
 | 
				
			||||||
                    this.$data.existingDoc,
 | 
					        if (this.$data.inputTitle) {
 | 
				
			||||||
                );
 | 
					          if (!this.$data.inputTitle?.value) {
 | 
				
			||||||
                if (this.$data.inputTitle) {
 | 
					            this.$data.inputTitle.value = file_name;
 | 
				
			||||||
                    if (!this.$data.inputTitle?.value) {
 | 
					          }
 | 
				
			||||||
                        this.$data.inputTitle.value = file_name;
 | 
					        }
 | 
				
			||||||
                    }
 | 
					      },
 | 
				
			||||||
                }
 | 
					      removeDocument: function (object: StoredObject): void {
 | 
				
			||||||
            },
 | 
					        console.log("catch remove document", object);
 | 
				
			||||||
            removeDocument: function (object: StoredObject): void {
 | 
					        input_stored_object.value = "";
 | 
				
			||||||
                console.log("catch remove document", object);
 | 
					        this.$data.existingDoc = undefined;
 | 
				
			||||||
                input_stored_object.value = "";
 | 
					        console.log("collectionEntry", collectionEntry);
 | 
				
			||||||
                this.$data.existingDoc = undefined;
 | 
					 | 
				
			||||||
                console.log("collectionEntry", collectionEntry);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (null !== collectionEntry) {
 | 
					        if (null !== collectionEntry) {
 | 
				
			||||||
                    console.log("will remove collection");
 | 
					          console.log("will remove collection");
 | 
				
			||||||
                    collectionEntry.remove();
 | 
					          collectionEntry.remove();
 | 
				
			||||||
                }
 | 
					        }
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app.use(i18n).mount(app_container);
 | 
					  app.use(i18n).mount(app_container);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
window.addEventListener("collection-add-entry", ((
 | 
					window.addEventListener("collection-add-entry", ((
 | 
				
			||||||
    e: CustomEvent<CollectionEventPayload>,
 | 
					  e: CustomEvent<CollectionEventPayload>,
 | 
				
			||||||
) => {
 | 
					) => {
 | 
				
			||||||
    const detail = e.detail;
 | 
					  const detail = e.detail;
 | 
				
			||||||
    const divElement: null | HTMLDivElement = detail.entry.querySelector(
 | 
					  const divElement: null | HTMLDivElement = detail.entry.querySelector(
 | 
				
			||||||
        "div[data-stored-object]",
 | 
					    "div[data-stored-object]",
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null === divElement) {
 | 
					  if (null === divElement) {
 | 
				
			||||||
        throw new Error("div[data-stored-object] not found");
 | 
					    throw new Error("div[data-stored-object] not found");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    startApp(divElement, detail.entry);
 | 
					  startApp(divElement, detail.entry);
 | 
				
			||||||
}) as EventListener);
 | 
					}) as EventListener);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener("DOMContentLoaded", () => {
 | 
					window.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
    const upload_inputs: NodeListOf<HTMLDivElement> = document.querySelectorAll(
 | 
					  const upload_inputs: NodeListOf<HTMLDivElement> = document.querySelectorAll(
 | 
				
			||||||
        "div[data-stored-object]",
 | 
					    "div[data-stored-object]",
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upload_inputs.forEach((input: HTMLDivElement): void => {
 | 
					  upload_inputs.forEach((input: HTMLDivElement): void => {
 | 
				
			||||||
        // test for a parent to check if this is a collection entry
 | 
					    // test for a parent to check if this is a collection entry
 | 
				
			||||||
        let collectionEntry: null | HTMLLIElement = null;
 | 
					    let collectionEntry: null | HTMLLIElement = null;
 | 
				
			||||||
        const parent = input.parentElement;
 | 
					    const parent = input.parentElement;
 | 
				
			||||||
        console.log("parent", parent);
 | 
					    console.log("parent", parent);
 | 
				
			||||||
        if (null !== parent) {
 | 
					    if (null !== parent) {
 | 
				
			||||||
            const grandParent = parent.parentElement;
 | 
					      const grandParent = parent.parentElement;
 | 
				
			||||||
            console.log("grandParent", grandParent);
 | 
					      console.log("grandParent", grandParent);
 | 
				
			||||||
            if (null !== grandParent) {
 | 
					      if (null !== grandParent) {
 | 
				
			||||||
                if (
 | 
					        if (
 | 
				
			||||||
                    grandParent.tagName.toLowerCase() === "li" &&
 | 
					          grandParent.tagName.toLowerCase() === "li" &&
 | 
				
			||||||
                    grandParent.classList.contains("entry")
 | 
					          grandParent.classList.contains("entry")
 | 
				
			||||||
                ) {
 | 
					        ) {
 | 
				
			||||||
                    collectionEntry = grandParent as HTMLLIElement;
 | 
					          collectionEntry = grandParent as HTMLLIElement;
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        startApp(input, collectionEntry);
 | 
					      }
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
 | 
					    startApp(input, collectionEntry);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {};
 | 
					export {};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,26 +9,26 @@ import ToastPlugin from "vue-toast-notification";
 | 
				
			|||||||
const i18n = _createI18n({});
 | 
					const i18n = _createI18n({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener("DOMContentLoaded", function (e) {
 | 
					window.addEventListener("DOMContentLoaded", function (e) {
 | 
				
			||||||
    document
 | 
					  document
 | 
				
			||||||
        .querySelectorAll<HTMLDivElement>("div[data-download-button-single]")
 | 
					    .querySelectorAll<HTMLDivElement>("div[data-download-button-single]")
 | 
				
			||||||
        .forEach((el) => {
 | 
					    .forEach((el) => {
 | 
				
			||||||
            const storedObject = JSON.parse(
 | 
					      const storedObject = JSON.parse(
 | 
				
			||||||
                el.dataset.storedObject as string,
 | 
					        el.dataset.storedObject as string,
 | 
				
			||||||
            ) as StoredObject;
 | 
					      ) as StoredObject;
 | 
				
			||||||
            const title = el.dataset.title as string;
 | 
					      const title = el.dataset.title as string;
 | 
				
			||||||
            const app = createApp({
 | 
					      const app = createApp({
 | 
				
			||||||
                components: { DownloadButton },
 | 
					        components: { DownloadButton },
 | 
				
			||||||
                data() {
 | 
					        data() {
 | 
				
			||||||
                    return {
 | 
					          return {
 | 
				
			||||||
                        storedObject,
 | 
					            storedObject,
 | 
				
			||||||
                        title,
 | 
					            title,
 | 
				
			||||||
                        classes: { btn: true, "btn-outline-primary": true },
 | 
					            classes: { btn: true, "btn-outline-primary": true },
 | 
				
			||||||
                    };
 | 
					          };
 | 
				
			||||||
                },
 | 
					        },
 | 
				
			||||||
                template:
 | 
					        template:
 | 
				
			||||||
                    '<download-button :stored-object="storedObject" :at-version="storedObject.currentVersion" :classes="classes" :filename="title" :direct-download="true"></download-button>',
 | 
					          '<download-button :stored-object="storedObject" :at-version="storedObject.currentVersion" :classes="classes" :filename="title" :direct-download="true"></download-button>',
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.use(i18n).use(ToastPlugin).mount(el);
 | 
					      app.use(i18n).use(ToastPlugin).mount(el);
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,66 +8,66 @@ import ToastPlugin from "vue-toast-notification";
 | 
				
			|||||||
const i18n = _createI18n({});
 | 
					const i18n = _createI18n({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener("DOMContentLoaded", function (e) {
 | 
					window.addEventListener("DOMContentLoaded", function (e) {
 | 
				
			||||||
    document
 | 
					  document
 | 
				
			||||||
        .querySelectorAll<HTMLDivElement>("div[data-download-buttons]")
 | 
					    .querySelectorAll<HTMLDivElement>("div[data-download-buttons]")
 | 
				
			||||||
        .forEach((el) => {
 | 
					    .forEach((el) => {
 | 
				
			||||||
            const app = createApp({
 | 
					      const app = createApp({
 | 
				
			||||||
                components: { DocumentActionButtonsGroup },
 | 
					        components: { DocumentActionButtonsGroup },
 | 
				
			||||||
                data() {
 | 
					        data() {
 | 
				
			||||||
                    const datasets = el.dataset as {
 | 
					          const datasets = el.dataset as {
 | 
				
			||||||
                        filename: string;
 | 
					            filename: string;
 | 
				
			||||||
                        canEdit: string;
 | 
					            canEdit: string;
 | 
				
			||||||
                        storedObject: string;
 | 
					            storedObject: string;
 | 
				
			||||||
                        buttonSmall: string;
 | 
					            buttonSmall: string;
 | 
				
			||||||
                        davLink: string;
 | 
					            davLink: string;
 | 
				
			||||||
                        davLinkExpiration: string;
 | 
					            davLinkExpiration: string;
 | 
				
			||||||
                    };
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    const storedObject = JSON.parse(
 | 
					          const storedObject = JSON.parse(
 | 
				
			||||||
                            datasets.storedObject,
 | 
					              datasets.storedObject,
 | 
				
			||||||
                        ) as StoredObject,
 | 
					            ) as StoredObject,
 | 
				
			||||||
                        filename = datasets.filename,
 | 
					            filename = datasets.filename,
 | 
				
			||||||
                        canEdit = datasets.canEdit === "1",
 | 
					            canEdit = datasets.canEdit === "1",
 | 
				
			||||||
                        small = datasets.buttonSmall === "1",
 | 
					            small = datasets.buttonSmall === "1",
 | 
				
			||||||
                        davLink =
 | 
					            davLink =
 | 
				
			||||||
                            "davLink" in datasets && datasets.davLink !== ""
 | 
					              "davLink" in datasets && datasets.davLink !== ""
 | 
				
			||||||
                                ? datasets.davLink
 | 
					                ? datasets.davLink
 | 
				
			||||||
                                : null,
 | 
					                : null,
 | 
				
			||||||
                        davLinkExpiration =
 | 
					            davLinkExpiration =
 | 
				
			||||||
                            "davLinkExpiration" in datasets
 | 
					              "davLinkExpiration" in datasets
 | 
				
			||||||
                                ? Number.parseInt(datasets.davLinkExpiration)
 | 
					                ? Number.parseInt(datasets.davLinkExpiration)
 | 
				
			||||||
                                : null;
 | 
					                : null;
 | 
				
			||||||
                    return {
 | 
					          return {
 | 
				
			||||||
                        storedObject,
 | 
					            storedObject,
 | 
				
			||||||
                        filename,
 | 
					            filename,
 | 
				
			||||||
                        canEdit,
 | 
					            canEdit,
 | 
				
			||||||
                        small,
 | 
					            small,
 | 
				
			||||||
                        davLink,
 | 
					            davLink,
 | 
				
			||||||
                        davLinkExpiration,
 | 
					            davLinkExpiration,
 | 
				
			||||||
                    };
 | 
					          };
 | 
				
			||||||
                },
 | 
					        },
 | 
				
			||||||
                template:
 | 
					        template:
 | 
				
			||||||
                    '<document-action-buttons-group :can-edit="canEdit" :filename="filename" :stored-object="storedObject" :small="small" :dav-link="davLink" :dav-link-expiration="davLinkExpiration" @on-stored-object-status-change="onStoredObjectStatusChange"></document-action-buttons-group>',
 | 
					          '<document-action-buttons-group :can-edit="canEdit" :filename="filename" :stored-object="storedObject" :small="small" :dav-link="davLink" :dav-link-expiration="davLinkExpiration" @on-stored-object-status-change="onStoredObjectStatusChange"></document-action-buttons-group>',
 | 
				
			||||||
                methods: {
 | 
					        methods: {
 | 
				
			||||||
                    onStoredObjectStatusChange: function (
 | 
					          onStoredObjectStatusChange: function (
 | 
				
			||||||
                        newStatus: StoredObjectStatusChange,
 | 
					            newStatus: StoredObjectStatusChange,
 | 
				
			||||||
                    ): void {
 | 
					          ): void {
 | 
				
			||||||
                        this.$data.storedObject.status = newStatus.status;
 | 
					            this.$data.storedObject.status = newStatus.status;
 | 
				
			||||||
                        this.$data.storedObject.filename = newStatus.filename;
 | 
					            this.$data.storedObject.filename = newStatus.filename;
 | 
				
			||||||
                        this.$data.storedObject.type = newStatus.type;
 | 
					            this.$data.storedObject.type = newStatus.type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        // remove eventual div which inform pending status
 | 
					            // remove eventual div which inform pending status
 | 
				
			||||||
                        document
 | 
					            document
 | 
				
			||||||
                            .querySelectorAll(
 | 
					              .querySelectorAll(
 | 
				
			||||||
                                `[data-docgen-is-pending="${this.$data.storedObject.id}"]`,
 | 
					                `[data-docgen-is-pending="${this.$data.storedObject.id}"]`,
 | 
				
			||||||
                            )
 | 
					              )
 | 
				
			||||||
                            .forEach(function (el) {
 | 
					              .forEach(function (el) {
 | 
				
			||||||
                                el.remove();
 | 
					                el.remove();
 | 
				
			||||||
                            });
 | 
					              });
 | 
				
			||||||
                    },
 | 
					          },
 | 
				
			||||||
                },
 | 
					        },
 | 
				
			||||||
            });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.use(i18n).use(ToastPlugin).mount(el);
 | 
					      app.use(i18n).use(ToastPlugin).mount(el);
 | 
				
			||||||
        });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { DateTime } from "ChillMainAssets/types";
 | 
				
			|||||||
import { StoredObject } from "ChillDocStoreAssets/types/index";
 | 
					import { StoredObject } from "ChillDocStoreAssets/types/index";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocMetadata {
 | 
					export interface GenericDocMetadata {
 | 
				
			||||||
    isPresent: boolean;
 | 
					  isPresent: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -15,57 +15,57 @@ export interface EmptyMetadata extends GenericDocMetadata {}
 | 
				
			|||||||
 * Minimal Metadata for a GenericDoc with a normalizer
 | 
					 * Minimal Metadata for a GenericDoc with a normalizer
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export interface BaseMetadata extends GenericDocMetadata {
 | 
					export interface BaseMetadata extends GenericDocMetadata {
 | 
				
			||||||
    title: string;
 | 
					  title: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A generic doc is a document attached to a Person or an AccompanyingPeriod.
 | 
					 * A generic doc is a document attached to a Person or an AccompanyingPeriod.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export interface GenericDoc {
 | 
					export interface GenericDoc {
 | 
				
			||||||
    type: "doc_store_generic_doc";
 | 
					  type: "doc_store_generic_doc";
 | 
				
			||||||
    uniqueKey: string;
 | 
					  uniqueKey: string;
 | 
				
			||||||
    key: string;
 | 
					  key: string;
 | 
				
			||||||
    identifiers: object;
 | 
					  identifiers: object;
 | 
				
			||||||
    context: "person" | "accompanying-period";
 | 
					  context: "person" | "accompanying-period";
 | 
				
			||||||
    doc_date: DateTime;
 | 
					  doc_date: DateTime;
 | 
				
			||||||
    metadata: GenericDocMetadata;
 | 
					  metadata: GenericDocMetadata;
 | 
				
			||||||
    storedObject: StoredObject | null;
 | 
					  storedObject: StoredObject | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingPeriod extends GenericDoc {
 | 
					export interface GenericDocForAccompanyingPeriod extends GenericDoc {
 | 
				
			||||||
    context: "accompanying-period";
 | 
					  context: "accompanying-period";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface BaseMetadataWithHtml extends BaseMetadata {
 | 
					interface BaseMetadataWithHtml extends BaseMetadata {
 | 
				
			||||||
    html: string;
 | 
					  html: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingCourseDocument
 | 
					export interface GenericDocForAccompanyingCourseDocument
 | 
				
			||||||
    extends GenericDocForAccompanyingPeriod {
 | 
					  extends GenericDocForAccompanyingPeriod {
 | 
				
			||||||
    key: "accompanying_course_document";
 | 
					  key: "accompanying_course_document";
 | 
				
			||||||
    metadata: BaseMetadataWithHtml;
 | 
					  metadata: BaseMetadataWithHtml;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingCourseActivityDocument
 | 
					export interface GenericDocForAccompanyingCourseActivityDocument
 | 
				
			||||||
    extends GenericDocForAccompanyingPeriod {
 | 
					  extends GenericDocForAccompanyingPeriod {
 | 
				
			||||||
    key: "accompanying_course_activity_document";
 | 
					  key: "accompanying_course_activity_document";
 | 
				
			||||||
    metadata: BaseMetadataWithHtml;
 | 
					  metadata: BaseMetadataWithHtml;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingCourseCalendarDocument
 | 
					export interface GenericDocForAccompanyingCourseCalendarDocument
 | 
				
			||||||
    extends GenericDocForAccompanyingPeriod {
 | 
					  extends GenericDocForAccompanyingPeriod {
 | 
				
			||||||
    key: "accompanying_course_calendar_document";
 | 
					  key: "accompanying_course_calendar_document";
 | 
				
			||||||
    metadata: BaseMetadataWithHtml;
 | 
					  metadata: BaseMetadataWithHtml;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingCoursePersonDocument
 | 
					export interface GenericDocForAccompanyingCoursePersonDocument
 | 
				
			||||||
    extends GenericDocForAccompanyingPeriod {
 | 
					  extends GenericDocForAccompanyingPeriod {
 | 
				
			||||||
    key: "person_document";
 | 
					  key: "person_document";
 | 
				
			||||||
    metadata: BaseMetadataWithHtml;
 | 
					  metadata: BaseMetadataWithHtml;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDocForAccompanyingCourseWorkEvaluationDocument
 | 
					export interface GenericDocForAccompanyingCourseWorkEvaluationDocument
 | 
				
			||||||
    extends GenericDocForAccompanyingPeriod {
 | 
					  extends GenericDocForAccompanyingPeriod {
 | 
				
			||||||
    key: "accompanying_period_work_evaluation_document";
 | 
					  key: "accompanying_period_work_evaluation_document";
 | 
				
			||||||
    metadata: BaseMetadataWithHtml;
 | 
					  metadata: BaseMetadataWithHtml;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,73 +4,73 @@ import { SignedUrlGet } from "ChillDocStoreAssets/vuejs/StoredObjectButton/helpe
 | 
				
			|||||||
export type StoredObjectStatus = "empty" | "ready" | "failure" | "pending";
 | 
					export type StoredObjectStatus = "empty" | "ready" | "failure" | "pending";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObject {
 | 
					export interface StoredObject {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    title: string | null;
 | 
					  title: string | null;
 | 
				
			||||||
    uuid: string;
 | 
					  uuid: string;
 | 
				
			||||||
    prefix: string;
 | 
					  prefix: string;
 | 
				
			||||||
    status: StoredObjectStatus;
 | 
					  status: StoredObjectStatus;
 | 
				
			||||||
    currentVersion:
 | 
					  currentVersion:
 | 
				
			||||||
        | null
 | 
					    | null
 | 
				
			||||||
        | StoredObjectVersionCreated
 | 
					    | StoredObjectVersionCreated
 | 
				
			||||||
        | StoredObjectVersionPersisted;
 | 
					    | StoredObjectVersionPersisted;
 | 
				
			||||||
    totalVersions: number;
 | 
					  totalVersions: number;
 | 
				
			||||||
    datas: object;
 | 
					  datas: object;
 | 
				
			||||||
    /** @deprecated */
 | 
					  /** @deprecated */
 | 
				
			||||||
    creationDate: DateTime;
 | 
					  creationDate: DateTime;
 | 
				
			||||||
    createdAt: DateTime | null;
 | 
					  createdAt: DateTime | null;
 | 
				
			||||||
    createdBy: User | null;
 | 
					  createdBy: User | null;
 | 
				
			||||||
    _permissions: {
 | 
					  _permissions: {
 | 
				
			||||||
        canEdit: boolean;
 | 
					    canEdit: boolean;
 | 
				
			||||||
        canSee: boolean;
 | 
					    canSee: boolean;
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
    _links?: {
 | 
					  _links?: {
 | 
				
			||||||
        dav_link?: {
 | 
					    dav_link?: {
 | 
				
			||||||
            href: string;
 | 
					      href: string;
 | 
				
			||||||
            expiration: number;
 | 
					      expiration: number;
 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        downloadLink?: SignedUrlGet;
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					    downloadLink?: SignedUrlGet;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectVersion {
 | 
					export interface StoredObjectVersion {
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * filename of the object in the object storage
 | 
					   * filename of the object in the object storage
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    filename: string;
 | 
					  filename: string;
 | 
				
			||||||
    iv: number[];
 | 
					  iv: number[];
 | 
				
			||||||
    keyInfos: JsonWebKey;
 | 
					  keyInfos: JsonWebKey;
 | 
				
			||||||
    type: string;
 | 
					  type: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectVersionCreated extends StoredObjectVersion {
 | 
					export interface StoredObjectVersionCreated extends StoredObjectVersion {
 | 
				
			||||||
    persisted: false;
 | 
					  persisted: false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectVersionPersisted
 | 
					export interface StoredObjectVersionPersisted
 | 
				
			||||||
    extends StoredObjectVersionCreated {
 | 
					  extends StoredObjectVersionCreated {
 | 
				
			||||||
    version: number;
 | 
					  version: number;
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    createdAt: DateTime | null;
 | 
					  createdAt: DateTime | null;
 | 
				
			||||||
    createdBy: User | null;
 | 
					  createdBy: User | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectStatusChange {
 | 
					export interface StoredObjectStatusChange {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    filename: string;
 | 
					  filename: string;
 | 
				
			||||||
    status: StoredObjectStatus;
 | 
					  status: StoredObjectStatus;
 | 
				
			||||||
    type: string;
 | 
					  type: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectVersionWithPointInTime
 | 
					export interface StoredObjectVersionWithPointInTime
 | 
				
			||||||
    extends StoredObjectVersionPersisted {
 | 
					  extends StoredObjectVersionPersisted {
 | 
				
			||||||
    "point-in-times": StoredObjectPointInTime[];
 | 
					  "point-in-times": StoredObjectPointInTime[];
 | 
				
			||||||
    "from-restored": StoredObjectVersionPersisted | null;
 | 
					  "from-restored": StoredObjectVersionPersisted | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StoredObjectPointInTime {
 | 
					export interface StoredObjectPointInTime {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    byUser: User | null;
 | 
					  byUser: User | null;
 | 
				
			||||||
    reason: "keep-before-conversion" | "keep-by-user";
 | 
					  reason: "keep-before-conversion" | "keep-by-user";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -82,63 +82,63 @@ export type WopiEditButtonExecutableBeforeLeaveFunction = () => Promise<void>;
 | 
				
			|||||||
 * Object containing information for performering a POST request to a swift object store
 | 
					 * Object containing information for performering a POST request to a swift object store
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export interface PostStoreObjectSignature {
 | 
					export interface PostStoreObjectSignature {
 | 
				
			||||||
    method: "POST";
 | 
					  method: "POST";
 | 
				
			||||||
    max_file_size: number;
 | 
					  max_file_size: number;
 | 
				
			||||||
    max_file_count: 1;
 | 
					  max_file_count: 1;
 | 
				
			||||||
    expires: number;
 | 
					  expires: number;
 | 
				
			||||||
    submit_delay: 180;
 | 
					  submit_delay: 180;
 | 
				
			||||||
    redirect: string;
 | 
					  redirect: string;
 | 
				
			||||||
    prefix: string;
 | 
					  prefix: string;
 | 
				
			||||||
    url: string;
 | 
					  url: string;
 | 
				
			||||||
    signature: string;
 | 
					  signature: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PDFPage {
 | 
					export interface PDFPage {
 | 
				
			||||||
    index: number;
 | 
					  index: number;
 | 
				
			||||||
    width: number;
 | 
					  width: number;
 | 
				
			||||||
    height: number;
 | 
					  height: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export interface SignatureZone {
 | 
					export interface SignatureZone {
 | 
				
			||||||
    index: number | null;
 | 
					  index: number | null;
 | 
				
			||||||
    x: number;
 | 
					  x: number;
 | 
				
			||||||
    y: number;
 | 
					  y: number;
 | 
				
			||||||
    width: number;
 | 
					  width: number;
 | 
				
			||||||
    height: number;
 | 
					  height: number;
 | 
				
			||||||
    PDFPage: PDFPage;
 | 
					  PDFPage: PDFPage;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Signature {
 | 
					export interface Signature {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    zones: SignatureZone[];
 | 
					  zones: SignatureZone[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SignedState =
 | 
					export type SignedState =
 | 
				
			||||||
    | "pending"
 | 
					  | "pending"
 | 
				
			||||||
    | "signed"
 | 
					  | "signed"
 | 
				
			||||||
    | "rejected"
 | 
					  | "rejected"
 | 
				
			||||||
    | "canceled"
 | 
					  | "canceled"
 | 
				
			||||||
    | "error";
 | 
					  | "error";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface CheckSignature {
 | 
					export interface CheckSignature {
 | 
				
			||||||
    state: SignedState;
 | 
					  state: SignedState;
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type CanvasEvent = "select" | "add";
 | 
					export type CanvasEvent = "select" | "add";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ZoomLevel {
 | 
					export interface ZoomLevel {
 | 
				
			||||||
    id: number;
 | 
					  id: number;
 | 
				
			||||||
    zoom: number;
 | 
					  zoom: number;
 | 
				
			||||||
    label: {
 | 
					  label: {
 | 
				
			||||||
        fr?: string;
 | 
					    fr?: string;
 | 
				
			||||||
        nl?: string;
 | 
					    nl?: string;
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GenericDoc {
 | 
					export interface GenericDoc {
 | 
				
			||||||
    type: "doc_store_generic_doc";
 | 
					  type: "doc_store_generic_doc";
 | 
				
			||||||
    key: string;
 | 
					  key: string;
 | 
				
			||||||
    context: "person" | "accompanying-period";
 | 
					  context: "person" | "accompanying-period";
 | 
				
			||||||
    doc_date: DateTime;
 | 
					  doc_date: DateTime;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,67 +1,65 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div v-if="isButtonGroupDisplayable" class="btn-group">
 | 
					  <div v-if="isButtonGroupDisplayable" class="btn-group">
 | 
				
			||||||
        <button
 | 
					    <button
 | 
				
			||||||
            :class="
 | 
					      :class="
 | 
				
			||||||
                Object.assign({
 | 
					        Object.assign({
 | 
				
			||||||
                    btn: true,
 | 
					          btn: true,
 | 
				
			||||||
                    'btn-outline-primary': true,
 | 
					          'btn-outline-primary': true,
 | 
				
			||||||
                    'dropdown-toggle': true,
 | 
					          'dropdown-toggle': true,
 | 
				
			||||||
                    'btn-sm': props.small,
 | 
					          'btn-sm': props.small,
 | 
				
			||||||
                })
 | 
					        })
 | 
				
			||||||
            "
 | 
					      "
 | 
				
			||||||
            type="button"
 | 
					      type="button"
 | 
				
			||||||
            data-bs-toggle="dropdown"
 | 
					      data-bs-toggle="dropdown"
 | 
				
			||||||
            aria-expanded="false"
 | 
					      aria-expanded="false"
 | 
				
			||||||
        >
 | 
					    >
 | 
				
			||||||
            Actions
 | 
					      Actions
 | 
				
			||||||
        </button>
 | 
					    </button>
 | 
				
			||||||
        <ul class="dropdown-menu">
 | 
					    <ul class="dropdown-menu">
 | 
				
			||||||
            <li v-if="isEditableOnline">
 | 
					      <li v-if="isEditableOnline">
 | 
				
			||||||
                <wopi-edit-button
 | 
					        <wopi-edit-button
 | 
				
			||||||
                    :stored-object="props.storedObject"
 | 
					          :stored-object="props.storedObject"
 | 
				
			||||||
                    :classes="{ 'dropdown-item': true }"
 | 
					          :classes="{ 'dropdown-item': true }"
 | 
				
			||||||
                    :execute-before-leave="props.executeBeforeLeave"
 | 
					          :execute-before-leave="props.executeBeforeLeave"
 | 
				
			||||||
                ></wopi-edit-button>
 | 
					        ></wopi-edit-button>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
            <li v-if="isEditableOnDesktop">
 | 
					      <li v-if="isEditableOnDesktop">
 | 
				
			||||||
                <desktop-edit-button
 | 
					        <desktop-edit-button
 | 
				
			||||||
                    :classes="{ 'dropdown-item': true }"
 | 
					          :classes="{ 'dropdown-item': true }"
 | 
				
			||||||
                    :edit-link="props.davLink"
 | 
					          :edit-link="props.davLink"
 | 
				
			||||||
                    :expiration-link="props.davLinkExpiration"
 | 
					          :expiration-link="props.davLinkExpiration"
 | 
				
			||||||
                ></desktop-edit-button>
 | 
					        ></desktop-edit-button>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
            <li v-if="isConvertibleToPdf">
 | 
					      <li v-if="isConvertibleToPdf">
 | 
				
			||||||
                <convert-button
 | 
					        <convert-button
 | 
				
			||||||
                    :stored-object="props.storedObject"
 | 
					          :stored-object="props.storedObject"
 | 
				
			||||||
                    :filename="filename"
 | 
					          :filename="filename"
 | 
				
			||||||
                    :classes="{ 'dropdown-item': true }"
 | 
					          :classes="{ 'dropdown-item': true }"
 | 
				
			||||||
                ></convert-button>
 | 
					        ></convert-button>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
            <li v-if="isDownloadable">
 | 
					      <li v-if="isDownloadable">
 | 
				
			||||||
                <download-button
 | 
					        <download-button
 | 
				
			||||||
                    :stored-object="props.storedObject"
 | 
					          :stored-object="props.storedObject"
 | 
				
			||||||
                    :at-version="props.storedObject.currentVersion"
 | 
					          :at-version="props.storedObject.currentVersion"
 | 
				
			||||||
                    :filename="filename"
 | 
					          :filename="filename"
 | 
				
			||||||
                    :classes="{ 'dropdown-item': true }"
 | 
					          :classes="{ 'dropdown-item': true }"
 | 
				
			||||||
                    :display-action-string-in-button="true"
 | 
					          :display-action-string-in-button="true"
 | 
				
			||||||
                ></download-button>
 | 
					        ></download-button>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
            <li v-if="isHistoryViewable">
 | 
					      <li v-if="isHistoryViewable">
 | 
				
			||||||
                <history-button
 | 
					        <history-button
 | 
				
			||||||
                    :stored-object="props.storedObject"
 | 
					          :stored-object="props.storedObject"
 | 
				
			||||||
                    :can-edit="
 | 
					          :can-edit="canEdit && props.storedObject._permissions.canEdit"
 | 
				
			||||||
                        canEdit && props.storedObject._permissions.canEdit
 | 
					        ></history-button>
 | 
				
			||||||
                    "
 | 
					      </li>
 | 
				
			||||||
                ></history-button>
 | 
					    </ul>
 | 
				
			||||||
            </li>
 | 
					  </div>
 | 
				
			||||||
        </ul>
 | 
					  <div v-else-if="'pending' === props.storedObject.status">
 | 
				
			||||||
    </div>
 | 
					    <div class="btn btn-outline-info">Génération en cours</div>
 | 
				
			||||||
    <div v-else-if="'pending' === props.storedObject.status">
 | 
					  </div>
 | 
				
			||||||
        <div class="btn btn-outline-info">Génération en cours</div>
 | 
					  <div v-else-if="'failure' === props.storedObject.status">
 | 
				
			||||||
    </div>
 | 
					    <div class="btn btn-outline-danger">La génération a échoué</div>
 | 
				
			||||||
    <div v-else-if="'failure' === props.storedObject.status">
 | 
					  </div>
 | 
				
			||||||
        <div class="btn btn-outline-danger">La génération a échoué</div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
@@ -70,68 +68,66 @@ import ConvertButton from "./StoredObjectButton/ConvertButton.vue";
 | 
				
			|||||||
import DownloadButton from "./StoredObjectButton/DownloadButton.vue";
 | 
					import DownloadButton from "./StoredObjectButton/DownloadButton.vue";
 | 
				
			||||||
import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue";
 | 
					import WopiEditButton from "./StoredObjectButton/WopiEditButton.vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    is_extension_editable,
 | 
					  is_extension_editable,
 | 
				
			||||||
    is_extension_viewable,
 | 
					  is_extension_viewable,
 | 
				
			||||||
    is_object_ready,
 | 
					  is_object_ready,
 | 
				
			||||||
} from "./StoredObjectButton/helpers";
 | 
					} from "./StoredObjectButton/helpers";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectStatusChange,
 | 
					  StoredObjectStatusChange,
 | 
				
			||||||
    StoredObjectVersion,
 | 
					  StoredObjectVersion,
 | 
				
			||||||
    WopiEditButtonExecutableBeforeLeaveFunction,
 | 
					  WopiEditButtonExecutableBeforeLeaveFunction,
 | 
				
			||||||
} from "../types";
 | 
					} from "../types";
 | 
				
			||||||
import DesktopEditButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DesktopEditButton.vue";
 | 
					import DesktopEditButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/DesktopEditButton.vue";
 | 
				
			||||||
import HistoryButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton.vue";
 | 
					import HistoryButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DocumentActionButtonsGroupConfig {
 | 
					interface DocumentActionButtonsGroupConfig {
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    small?: boolean;
 | 
					  small?: boolean;
 | 
				
			||||||
    canEdit?: boolean;
 | 
					  canEdit?: boolean;
 | 
				
			||||||
    canDownload?: boolean;
 | 
					  canDownload?: boolean;
 | 
				
			||||||
    canConvertPdf?: boolean;
 | 
					  canConvertPdf?: boolean;
 | 
				
			||||||
    returnPath?: string;
 | 
					  returnPath?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * Will be the filename displayed to the user when he·she download the document
 | 
					   * Will be the filename displayed to the user when he·she download the document
 | 
				
			||||||
     * (the document will be saved on his disk with this name)
 | 
					   * (the document will be saved on his disk with this name)
 | 
				
			||||||
     *
 | 
					   *
 | 
				
			||||||
     * If not set, 'document' will be used.
 | 
					   * If not set, 'document' will be used.
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    filename?: string;
 | 
					  filename?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * If set, will execute this function before leaving to the editor
 | 
					   * If set, will execute this function before leaving to the editor
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
 | 
					  executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * a link to download and edit file using webdav
 | 
					   * a link to download and edit file using webdav
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    davLink?: string;
 | 
					  davLink?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * the expiration date of the download, as a unix timestamp
 | 
					   * the expiration date of the download, as a unix timestamp
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    davLinkExpiration?: number;
 | 
					  davLinkExpiration?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit =
 | 
					const emit =
 | 
				
			||||||
    defineEmits<
 | 
					  defineEmits<
 | 
				
			||||||
        (
 | 
					    (
 | 
				
			||||||
            e: "onStoredObjectStatusChange",
 | 
					      e: "onStoredObjectStatusChange",
 | 
				
			||||||
            newStatus: StoredObjectStatusChange,
 | 
					      newStatus: StoredObjectStatusChange,
 | 
				
			||||||
        ) => void
 | 
					    ) => void
 | 
				
			||||||
    >();
 | 
					  >();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
 | 
					const props = withDefaults(defineProps<DocumentActionButtonsGroupConfig>(), {
 | 
				
			||||||
    small: false,
 | 
					  small: false,
 | 
				
			||||||
    canEdit: true,
 | 
					  canEdit: true,
 | 
				
			||||||
    canDownload: true,
 | 
					  canDownload: true,
 | 
				
			||||||
    canConvertPdf: true,
 | 
					  canConvertPdf: true,
 | 
				
			||||||
    returnPath:
 | 
					  returnPath:
 | 
				
			||||||
        window.location.pathname +
 | 
					    window.location.pathname + window.location.search + window.location.hash,
 | 
				
			||||||
        window.location.search +
 | 
					 | 
				
			||||||
        window.location.hash,
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -145,93 +141,93 @@ let tryiesForReady = 0;
 | 
				
			|||||||
const maxTryiesForReady = 120;
 | 
					const maxTryiesForReady = 120;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isButtonGroupDisplayable = computed<boolean>(() => {
 | 
					const isButtonGroupDisplayable = computed<boolean>(() => {
 | 
				
			||||||
    return (
 | 
					  return (
 | 
				
			||||||
        isDownloadable.value ||
 | 
					    isDownloadable.value ||
 | 
				
			||||||
        isEditableOnline.value ||
 | 
					    isEditableOnline.value ||
 | 
				
			||||||
        isEditableOnDesktop.value ||
 | 
					    isEditableOnDesktop.value ||
 | 
				
			||||||
        isConvertibleToPdf.value
 | 
					    isConvertibleToPdf.value
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isDownloadable = computed<boolean>(() => {
 | 
					const isDownloadable = computed<boolean>(() => {
 | 
				
			||||||
    return (
 | 
					  return (
 | 
				
			||||||
        props.storedObject.status === "ready" ||
 | 
					    props.storedObject.status === "ready" ||
 | 
				
			||||||
        // happens when the stored object version is just added, but not persisted
 | 
					    // happens when the stored object version is just added, but not persisted
 | 
				
			||||||
        (props.storedObject.currentVersion !== null &&
 | 
					    (props.storedObject.currentVersion !== null &&
 | 
				
			||||||
            props.storedObject.status === "empty")
 | 
					      props.storedObject.status === "empty")
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isEditableOnline = computed<boolean>(() => {
 | 
					const isEditableOnline = computed<boolean>(() => {
 | 
				
			||||||
    return (
 | 
					  return (
 | 
				
			||||||
        props.storedObject.status === "ready" &&
 | 
					    props.storedObject.status === "ready" &&
 | 
				
			||||||
        props.storedObject._permissions.canEdit &&
 | 
					    props.storedObject._permissions.canEdit &&
 | 
				
			||||||
        props.canEdit &&
 | 
					    props.canEdit &&
 | 
				
			||||||
        props.storedObject.currentVersion !== null &&
 | 
					    props.storedObject.currentVersion !== null &&
 | 
				
			||||||
        is_extension_editable(props.storedObject.currentVersion.type) &&
 | 
					    is_extension_editable(props.storedObject.currentVersion.type) &&
 | 
				
			||||||
        props.storedObject.currentVersion.persisted !== false
 | 
					    props.storedObject.currentVersion.persisted !== false
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isEditableOnDesktop = computed<boolean>(() => {
 | 
					const isEditableOnDesktop = computed<boolean>(() => {
 | 
				
			||||||
    return isEditableOnline.value;
 | 
					  return isEditableOnline.value;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isConvertibleToPdf = computed<boolean>(() => {
 | 
					const isConvertibleToPdf = computed<boolean>(() => {
 | 
				
			||||||
    return (
 | 
					  return (
 | 
				
			||||||
        props.storedObject.status === "ready" &&
 | 
					    props.storedObject.status === "ready" &&
 | 
				
			||||||
        props.storedObject._permissions.canSee &&
 | 
					    props.storedObject._permissions.canSee &&
 | 
				
			||||||
        props.canConvertPdf &&
 | 
					    props.canConvertPdf &&
 | 
				
			||||||
        props.storedObject.currentVersion !== null &&
 | 
					    props.storedObject.currentVersion !== null &&
 | 
				
			||||||
        is_extension_viewable(props.storedObject.currentVersion.type) &&
 | 
					    is_extension_viewable(props.storedObject.currentVersion.type) &&
 | 
				
			||||||
        props.storedObject.currentVersion.type !== "application/pdf" &&
 | 
					    props.storedObject.currentVersion.type !== "application/pdf" &&
 | 
				
			||||||
        props.storedObject.currentVersion.persisted !== false
 | 
					    props.storedObject.currentVersion.persisted !== false
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isHistoryViewable = computed<boolean>(() => {
 | 
					const isHistoryViewable = computed<boolean>(() => {
 | 
				
			||||||
    return props.storedObject.status === "ready";
 | 
					  return props.storedObject.status === "ready";
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const checkForReady = function (): void {
 | 
					const checkForReady = function (): void {
 | 
				
			||||||
    if (
 | 
					  if (
 | 
				
			||||||
        "ready" === props.storedObject.status ||
 | 
					    "ready" === props.storedObject.status ||
 | 
				
			||||||
        "empty" === props.storedObject.status ||
 | 
					    "empty" === props.storedObject.status ||
 | 
				
			||||||
        "failure" === props.storedObject.status ||
 | 
					    "failure" === props.storedObject.status ||
 | 
				
			||||||
        // stop reloading if the page stays opened for a long time
 | 
					    // stop reloading if the page stays opened for a long time
 | 
				
			||||||
        tryiesForReady > maxTryiesForReady
 | 
					    tryiesForReady > maxTryiesForReady
 | 
				
			||||||
    ) {
 | 
					  ) {
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tryiesForReady = tryiesForReady + 1;
 | 
					  tryiesForReady = tryiesForReady + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setTimeout(onObjectNewStatusCallback, 5000);
 | 
					  setTimeout(onObjectNewStatusCallback, 5000);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onObjectNewStatusCallback = async function (): Promise<void> {
 | 
					const onObjectNewStatusCallback = async function (): Promise<void> {
 | 
				
			||||||
    if (props.storedObject.status === "stored_object_created") {
 | 
					  if (props.storedObject.status === "stored_object_created") {
 | 
				
			||||||
        return Promise.resolve();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const new_status = await is_object_ready(props.storedObject);
 | 
					 | 
				
			||||||
    if (props.storedObject.status !== new_status.status) {
 | 
					 | 
				
			||||||
        emit("onStoredObjectStatusChange", new_status);
 | 
					 | 
				
			||||||
        return Promise.resolve();
 | 
					 | 
				
			||||||
    } else if ("failure" === new_status.status) {
 | 
					 | 
				
			||||||
        return Promise.resolve();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ("ready" !== new_status.status) {
 | 
					 | 
				
			||||||
        // we check for new status, unless it is ready
 | 
					 | 
				
			||||||
        checkForReady();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return Promise.resolve();
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const new_status = await is_object_ready(props.storedObject);
 | 
				
			||||||
 | 
					  if (props.storedObject.status !== new_status.status) {
 | 
				
			||||||
 | 
					    emit("onStoredObjectStatusChange", new_status);
 | 
				
			||||||
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  } else if ("failure" === new_status.status) {
 | 
				
			||||||
 | 
					    return Promise.resolve();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ("ready" !== new_status.status) {
 | 
				
			||||||
 | 
					    // we check for new status, unless it is ready
 | 
				
			||||||
 | 
					    checkForReady();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Promise.resolve();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
    checkForReady();
 | 
					  checkForReady();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,36 +4,36 @@ import { _createI18n } from "ChillMainAssets/vuejs/_js/i18n";
 | 
				
			|||||||
import App from "./App.vue";
 | 
					import App from "./App.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const appMessages = {
 | 
					const appMessages = {
 | 
				
			||||||
    fr: {
 | 
					  fr: {
 | 
				
			||||||
        yes: "Oui",
 | 
					    yes: "Oui",
 | 
				
			||||||
        are_you_sure: "Êtes-vous sûr·e?",
 | 
					    are_you_sure: "Êtes-vous sûr·e?",
 | 
				
			||||||
        you_are_going_to_sign: "Vous allez signer le document",
 | 
					    you_are_going_to_sign: "Vous allez signer le document",
 | 
				
			||||||
        signature_confirmation: "Confirmation de la signature",
 | 
					    signature_confirmation: "Confirmation de la signature",
 | 
				
			||||||
        sign: "Signer",
 | 
					    sign: "Signer",
 | 
				
			||||||
        choose_another_signature: "Choisir une autre zone",
 | 
					    choose_another_signature: "Choisir une autre zone",
 | 
				
			||||||
        cancel: "Annuler",
 | 
					    cancel: "Annuler",
 | 
				
			||||||
        last_sign_zone: "Zone de signature précédente",
 | 
					    last_sign_zone: "Zone de signature précédente",
 | 
				
			||||||
        next_sign_zone: "Zone de signature suivante",
 | 
					    next_sign_zone: "Zone de signature suivante",
 | 
				
			||||||
        add_sign_zone: "Ajouter une zone de signature",
 | 
					    add_sign_zone: "Ajouter une zone de signature",
 | 
				
			||||||
        click_on_document: "Cliquer sur le document",
 | 
					    click_on_document: "Cliquer sur le document",
 | 
				
			||||||
        last_zone: "Zone précédente",
 | 
					    last_zone: "Zone précédente",
 | 
				
			||||||
        next_zone: "Zone suivante",
 | 
					    next_zone: "Zone suivante",
 | 
				
			||||||
        add_zone: "Ajouter une zone",
 | 
					    add_zone: "Ajouter une zone",
 | 
				
			||||||
        another_zone: "Autre zone",
 | 
					    another_zone: "Autre zone",
 | 
				
			||||||
        electronic_signature_in_progress: "Signature électronique en cours...",
 | 
					    electronic_signature_in_progress: "Signature électronique en cours...",
 | 
				
			||||||
        loading: "Chargement...",
 | 
					    loading: "Chargement...",
 | 
				
			||||||
        remove_sign_zone: "Enlever la zone",
 | 
					    remove_sign_zone: "Enlever la zone",
 | 
				
			||||||
        return: "Retour",
 | 
					    return: "Retour",
 | 
				
			||||||
        see_all_pages: "Voir toutes les pages",
 | 
					    see_all_pages: "Voir toutes les pages",
 | 
				
			||||||
        all_pages: "Toutes les pages",
 | 
					    all_pages: "Toutes les pages",
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const i18n = _createI18n(appMessages);
 | 
					const i18n = _createI18n(appMessages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = createApp({
 | 
					const app = createApp({
 | 
				
			||||||
    template: `<app></app>`,
 | 
					  template: `<app></app>`,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
    .use(i18n)
 | 
					  .use(i18n)
 | 
				
			||||||
    .component("app", App)
 | 
					  .component("app", App)
 | 
				
			||||||
    .mount("#document-signature");
 | 
					  .mount("#document-signature");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,208 +1,206 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { StoredObject, StoredObjectVersionCreated } from "../../types";
 | 
					import { StoredObject, StoredObjectVersionCreated } from "../../types";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    encryptFile,
 | 
					  encryptFile,
 | 
				
			||||||
    fetchNewStoredObject,
 | 
					  fetchNewStoredObject,
 | 
				
			||||||
    uploadVersion,
 | 
					  uploadVersion,
 | 
				
			||||||
} from "../../js/async-upload/uploader";
 | 
					} from "../../js/async-upload/uploader";
 | 
				
			||||||
import { computed, ref, Ref } from "vue";
 | 
					import { computed, ref, Ref } from "vue";
 | 
				
			||||||
import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
 | 
					import FileIcon from "ChillDocStoreAssets/vuejs/FileIcon.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DropFileConfig {
 | 
					interface DropFileConfig {
 | 
				
			||||||
    existingDoc?: StoredObject;
 | 
					  existingDoc?: StoredObject;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
					const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
				
			||||||
    existingDoc: null,
 | 
					  existingDoc: null,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit =
 | 
					const emit =
 | 
				
			||||||
    defineEmits<
 | 
					  defineEmits<
 | 
				
			||||||
        (
 | 
					    (
 | 
				
			||||||
            e: "addDocument",
 | 
					      e: "addDocument",
 | 
				
			||||||
            {
 | 
					      {
 | 
				
			||||||
                stored_object_version: StoredObjectVersionCreated,
 | 
					        stored_object_version: StoredObjectVersionCreated,
 | 
				
			||||||
                stored_object: StoredObject,
 | 
					        stored_object: StoredObject,
 | 
				
			||||||
                file_name: string,
 | 
					        file_name: string,
 | 
				
			||||||
            },
 | 
					      },
 | 
				
			||||||
        ) => void
 | 
					    ) => void
 | 
				
			||||||
    >();
 | 
					  >();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const is_dragging: Ref<boolean> = ref(false);
 | 
					const is_dragging: Ref<boolean> = ref(false);
 | 
				
			||||||
const uploading: Ref<boolean> = ref(false);
 | 
					const uploading: Ref<boolean> = ref(false);
 | 
				
			||||||
const display_filename: Ref<string | null> = ref(null);
 | 
					const display_filename: Ref<string | null> = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const has_existing_doc = computed<boolean>(() => {
 | 
					const has_existing_doc = computed<boolean>(() => {
 | 
				
			||||||
    return props.existingDoc !== undefined && props.existingDoc !== null;
 | 
					  return props.existingDoc !== undefined && props.existingDoc !== null;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onDragOver = (e: Event) => {
 | 
					const onDragOver = (e: Event) => {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_dragging.value = true;
 | 
					  is_dragging.value = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onDragLeave = (e: Event) => {
 | 
					const onDragLeave = (e: Event) => {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    is_dragging.value = false;
 | 
					  is_dragging.value = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onDrop = (e: DragEvent) => {
 | 
					const onDrop = (e: DragEvent) => {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const files = e.dataTransfer?.files;
 | 
					  const files = e.dataTransfer?.files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null === files || undefined === files) {
 | 
					  if (null === files || undefined === files) {
 | 
				
			||||||
        console.error("no files transferred", e.dataTransfer);
 | 
					    console.error("no files transferred", e.dataTransfer);
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (files.length === 0) {
 | 
					  if (files.length === 0) {
 | 
				
			||||||
        console.error("no files given");
 | 
					    console.error("no files given");
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    handleFile(files[0]);
 | 
					  handleFile(files[0]);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onZoneClick = (e: Event) => {
 | 
					const onZoneClick = (e: Event) => {
 | 
				
			||||||
    e.stopPropagation();
 | 
					  e.stopPropagation();
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const input = document.createElement("input");
 | 
					  const input = document.createElement("input");
 | 
				
			||||||
    input.type = "file";
 | 
					  input.type = "file";
 | 
				
			||||||
    input.addEventListener("change", onFileChange);
 | 
					  input.addEventListener("change", onFileChange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    input.click();
 | 
					  input.click();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onFileChange = async (event: Event): Promise<void> => {
 | 
					const onFileChange = async (event: Event): Promise<void> => {
 | 
				
			||||||
    const input = event.target as HTMLInputElement;
 | 
					  const input = event.target as HTMLInputElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (input.files && input.files[0]) {
 | 
					  if (input.files && input.files[0]) {
 | 
				
			||||||
        console.log("file added", input.files[0]);
 | 
					    console.log("file added", input.files[0]);
 | 
				
			||||||
        const file = input.files[0];
 | 
					    const file = input.files[0];
 | 
				
			||||||
        await handleFile(file);
 | 
					    await handleFile(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Promise.resolve();
 | 
					    return Promise.resolve();
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    throw "No file given";
 | 
					  throw "No file given";
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handleFile = async (file: File): Promise<void> => {
 | 
					const handleFile = async (file: File): Promise<void> => {
 | 
				
			||||||
    uploading.value = true;
 | 
					  uploading.value = true;
 | 
				
			||||||
    display_filename.value = file.name;
 | 
					  display_filename.value = file.name;
 | 
				
			||||||
    const type = file.type;
 | 
					  const type = file.type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // create a stored_object if not exists
 | 
					  // create a stored_object if not exists
 | 
				
			||||||
    let stored_object;
 | 
					  let stored_object;
 | 
				
			||||||
    if (null === props.existingDoc) {
 | 
					  if (null === props.existingDoc) {
 | 
				
			||||||
        stored_object = await fetchNewStoredObject();
 | 
					    stored_object = await fetchNewStoredObject();
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        stored_object = props.existingDoc;
 | 
					    stored_object = props.existingDoc;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const buffer = await file.arrayBuffer();
 | 
					  const buffer = await file.arrayBuffer();
 | 
				
			||||||
    const [encrypted, iv, jsonWebKey] = await encryptFile(buffer);
 | 
					  const [encrypted, iv, jsonWebKey] = await encryptFile(buffer);
 | 
				
			||||||
    const filename = await uploadVersion(encrypted, stored_object);
 | 
					  const filename = await uploadVersion(encrypted, stored_object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const stored_object_version: StoredObjectVersionCreated = {
 | 
					  const stored_object_version: StoredObjectVersionCreated = {
 | 
				
			||||||
        filename: filename,
 | 
					    filename: filename,
 | 
				
			||||||
        iv: Array.from(iv),
 | 
					    iv: Array.from(iv),
 | 
				
			||||||
        keyInfos: jsonWebKey,
 | 
					    keyInfos: jsonWebKey,
 | 
				
			||||||
        type: type,
 | 
					    type: type,
 | 
				
			||||||
        persisted: false,
 | 
					    persisted: false,
 | 
				
			||||||
    };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const fileName = file.name;
 | 
					  const fileName = file.name;
 | 
				
			||||||
    let file_name = "Nouveau document";
 | 
					  let file_name = "Nouveau document";
 | 
				
			||||||
    const file_name_split = fileName.split(".");
 | 
					  const file_name_split = fileName.split(".");
 | 
				
			||||||
    if (file_name_split.length > 1) {
 | 
					  if (file_name_split.length > 1) {
 | 
				
			||||||
        const extension = file_name_split
 | 
					    const extension = file_name_split
 | 
				
			||||||
            ? file_name_split[file_name_split.length - 1]
 | 
					      ? file_name_split[file_name_split.length - 1]
 | 
				
			||||||
            : "";
 | 
					      : "";
 | 
				
			||||||
        file_name = fileName.replace(extension, "").slice(0, -1);
 | 
					    file_name = fileName.replace(extension, "").slice(0, -1);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    emit("addDocument", {
 | 
					  emit("addDocument", {
 | 
				
			||||||
        stored_object,
 | 
					    stored_object,
 | 
				
			||||||
        stored_object_version,
 | 
					    stored_object_version,
 | 
				
			||||||
        file_name: file_name,
 | 
					    file_name: file_name,
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
    uploading.value = false;
 | 
					  uploading.value = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div class="drop-file">
 | 
					  <div class="drop-file">
 | 
				
			||||||
        <div
 | 
					    <div
 | 
				
			||||||
            v-if="!uploading"
 | 
					      v-if="!uploading"
 | 
				
			||||||
            :class="{ area: true, dragging: is_dragging }"
 | 
					      :class="{ area: true, dragging: is_dragging }"
 | 
				
			||||||
            @click="onZoneClick"
 | 
					      @click="onZoneClick"
 | 
				
			||||||
            @dragover="onDragOver"
 | 
					      @dragover="onDragOver"
 | 
				
			||||||
            @dragleave="onDragLeave"
 | 
					      @dragleave="onDragLeave"
 | 
				
			||||||
            @drop="onDrop"
 | 
					      @drop="onDrop"
 | 
				
			||||||
        >
 | 
					    >
 | 
				
			||||||
            <p v-if="has_existing_doc" class="file-icon">
 | 
					      <p v-if="has_existing_doc" class="file-icon">
 | 
				
			||||||
                <file-icon :type="props.existingDoc?.type"></file-icon>
 | 
					        <file-icon :type="props.existingDoc?.type"></file-icon>
 | 
				
			||||||
            </p>
 | 
					      </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <p v-if="display_filename !== null" class="display-filename">
 | 
					      <p v-if="display_filename !== null" class="display-filename">
 | 
				
			||||||
                {{ display_filename }}
 | 
					        {{ display_filename }}
 | 
				
			||||||
            </p>
 | 
					      </p>
 | 
				
			||||||
            <!-- todo i18n -->
 | 
					      <!-- todo i18n -->
 | 
				
			||||||
            <p v-if="has_existing_doc">
 | 
					      <p v-if="has_existing_doc">
 | 
				
			||||||
                Déposez un document ou cliquez ici pour remplacer le document
 | 
					        Déposez un document ou cliquez ici pour remplacer le document existant
 | 
				
			||||||
                existant
 | 
					      </p>
 | 
				
			||||||
            </p>
 | 
					      <p v-else>
 | 
				
			||||||
            <p v-else>
 | 
					        Déposez un document ou cliquez ici pour ouvrir le navigateur de fichier
 | 
				
			||||||
                Déposez un document ou cliquez ici pour ouvrir le navigateur de
 | 
					      </p>
 | 
				
			||||||
                fichier
 | 
					 | 
				
			||||||
            </p>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div v-else class="waiting">
 | 
					 | 
				
			||||||
            <i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
 | 
					 | 
				
			||||||
            <span class="sr-only">Loading...</span>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div v-else class="waiting">
 | 
				
			||||||
 | 
					      <i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
 | 
				
			||||||
 | 
					      <span class="sr-only">Loading...</span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
.drop-file {
 | 
					.drop-file {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .file-icon {
 | 
				
			||||||
 | 
					    font-size: xx-large;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .display-filename {
 | 
				
			||||||
 | 
					    font-variant: small-caps;
 | 
				
			||||||
 | 
					    font-weight: 200;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  & > .area,
 | 
				
			||||||
 | 
					  & > .waiting {
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    height: 10rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .file-icon {
 | 
					    display: flex;
 | 
				
			||||||
        font-size: xx-large;
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    justify-content: center;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p {
 | 
				
			||||||
 | 
					      // require for display in DropFileModal
 | 
				
			||||||
 | 
					      text-align: center;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .display-filename {
 | 
					  & > .area {
 | 
				
			||||||
        font-variant: small-caps;
 | 
					    border: 4px dashed #ccc;
 | 
				
			||||||
        font-weight: 200;
 | 
					
 | 
				
			||||||
    }
 | 
					    &.dragging {
 | 
				
			||||||
 | 
					      border: 4px dashed blue;
 | 
				
			||||||
    & > .area,
 | 
					 | 
				
			||||||
    & > .waiting {
 | 
					 | 
				
			||||||
        width: 100%;
 | 
					 | 
				
			||||||
        height: 10rem;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
        flex-direction: column;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        p {
 | 
					 | 
				
			||||||
            // require for display in DropFileModal
 | 
					 | 
				
			||||||
            text-align: center;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    & > .area {
 | 
					 | 
				
			||||||
        border: 4px dashed #ccc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        &.dragging {
 | 
					 | 
				
			||||||
            border: 4px dashed blue;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,24 +6,24 @@ import { computed, reactive } from "vue";
 | 
				
			|||||||
import { useToast } from "vue-toast-notification";
 | 
					import { useToast } from "vue-toast-notification";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DropFileConfig {
 | 
					interface DropFileConfig {
 | 
				
			||||||
    allowRemove: boolean;
 | 
					  allowRemove: boolean;
 | 
				
			||||||
    existingDoc?: StoredObject;
 | 
					  existingDoc?: StoredObject;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
					const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
				
			||||||
    allowRemove: false,
 | 
					  allowRemove: false,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    (
 | 
					  (
 | 
				
			||||||
        e: "addDocument",
 | 
					    e: "addDocument",
 | 
				
			||||||
        {
 | 
					    {
 | 
				
			||||||
            stored_object: StoredObject,
 | 
					      stored_object: StoredObject,
 | 
				
			||||||
            stored_object_version: StoredObjectVersion,
 | 
					      stored_object_version: StoredObjectVersion,
 | 
				
			||||||
            file_name: string,
 | 
					      file_name: string,
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
    ): void;
 | 
					  ): void;
 | 
				
			||||||
    (e: "removeDocument"): void;
 | 
					  (e: "removeDocument"): void;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const $toast = useToast();
 | 
					const $toast = useToast();
 | 
				
			||||||
@@ -33,67 +33,67 @@ const state = reactive({ showModal: false });
 | 
				
			|||||||
const modalClasses = { "modal-dialog-centered": true, "modal-md": true };
 | 
					const modalClasses = { "modal-dialog-centered": true, "modal-md": true };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const buttonState = computed<"add" | "replace">(() => {
 | 
					const buttonState = computed<"add" | "replace">(() => {
 | 
				
			||||||
    if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
					  if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
				
			||||||
        return "add";
 | 
					    return "add";
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return "replace";
 | 
					  return "replace";
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onAddDocument({
 | 
					function onAddDocument({
 | 
				
			||||||
    stored_object,
 | 
					  stored_object,
 | 
				
			||||||
    stored_object_version,
 | 
					  stored_object_version,
 | 
				
			||||||
    file_name,
 | 
					  file_name,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    stored_object: StoredObject;
 | 
					  stored_object: StoredObject;
 | 
				
			||||||
    stored_object_version: StoredObjectVersion;
 | 
					  stored_object_version: StoredObjectVersion;
 | 
				
			||||||
    file_name: string;
 | 
					  file_name: string;
 | 
				
			||||||
}): void {
 | 
					}): void {
 | 
				
			||||||
    const message =
 | 
					  const message =
 | 
				
			||||||
        buttonState.value === "add" ? "Document ajouté" : "Document remplacé";
 | 
					    buttonState.value === "add" ? "Document ajouté" : "Document remplacé";
 | 
				
			||||||
    $toast.success(message);
 | 
					  $toast.success(message);
 | 
				
			||||||
    emit("addDocument", { stored_object_version, stored_object, file_name });
 | 
					  emit("addDocument", { stored_object_version, stored_object, file_name });
 | 
				
			||||||
    state.showModal = false;
 | 
					  state.showModal = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onRemoveDocument(): void {
 | 
					function onRemoveDocument(): void {
 | 
				
			||||||
    emit("removeDocument");
 | 
					  emit("removeDocument");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function openModal(): void {
 | 
					function openModal(): void {
 | 
				
			||||||
    state.showModal = true;
 | 
					  state.showModal = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function closeModal(): void {
 | 
					function closeModal(): void {
 | 
				
			||||||
    state.showModal = false;
 | 
					  state.showModal = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <button
 | 
					  <button
 | 
				
			||||||
        v-if="buttonState === 'add'"
 | 
					    v-if="buttonState === 'add'"
 | 
				
			||||||
        @click="openModal"
 | 
					    @click="openModal"
 | 
				
			||||||
        class="btn btn-create"
 | 
					    class="btn btn-create"
 | 
				
			||||||
    >
 | 
					  >
 | 
				
			||||||
        Ajouter un document
 | 
					    Ajouter un document
 | 
				
			||||||
    </button>
 | 
					  </button>
 | 
				
			||||||
    <button v-else @click="openModal" class="btn btn-edit">
 | 
					  <button v-else @click="openModal" class="btn btn-edit">
 | 
				
			||||||
        Remplacer le document
 | 
					    Remplacer le document
 | 
				
			||||||
    </button>
 | 
					  </button>
 | 
				
			||||||
    <modal
 | 
					  <modal
 | 
				
			||||||
        v-if="state.showModal"
 | 
					    v-if="state.showModal"
 | 
				
			||||||
        :modal-dialog-class="modalClasses"
 | 
					    :modal-dialog-class="modalClasses"
 | 
				
			||||||
        @close="closeModal"
 | 
					    @close="closeModal"
 | 
				
			||||||
    >
 | 
					  >
 | 
				
			||||||
        <template v-slot:body>
 | 
					    <template v-slot:body>
 | 
				
			||||||
            <drop-file-widget
 | 
					      <drop-file-widget
 | 
				
			||||||
                :existing-doc="existingDoc"
 | 
					        :existing-doc="existingDoc"
 | 
				
			||||||
                :allow-remove="allowRemove"
 | 
					        :allow-remove="allowRemove"
 | 
				
			||||||
                @add-document="onAddDocument"
 | 
					        @add-document="onAddDocument"
 | 
				
			||||||
                @remove-document="onRemoveDocument"
 | 
					        @remove-document="onRemoveDocument"
 | 
				
			||||||
            ></drop-file-widget>
 | 
					      ></drop-file-widget>
 | 
				
			||||||
        </template>
 | 
					    </template>
 | 
				
			||||||
    </modal>
 | 
					  </modal>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,97 +5,97 @@ import DropFile from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFile.vue";
 | 
				
			|||||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
 | 
					import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DropFileConfig {
 | 
					interface DropFileConfig {
 | 
				
			||||||
    allowRemove: boolean;
 | 
					  allowRemove: boolean;
 | 
				
			||||||
    existingDoc?: StoredObject;
 | 
					  existingDoc?: StoredObject;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
					const props = withDefaults(defineProps<DropFileConfig>(), {
 | 
				
			||||||
    allowRemove: false,
 | 
					  allowRemove: false,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    (
 | 
					  (
 | 
				
			||||||
        e: "addDocument",
 | 
					    e: "addDocument",
 | 
				
			||||||
        {
 | 
					    {
 | 
				
			||||||
            stored_object: StoredObject,
 | 
					      stored_object: StoredObject,
 | 
				
			||||||
            stored_object_version: StoredObjectVersion,
 | 
					      stored_object_version: StoredObjectVersion,
 | 
				
			||||||
            file_name: string,
 | 
					      file_name: string,
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
    ): void;
 | 
					  ): void;
 | 
				
			||||||
    (e: "removeDocument"): void;
 | 
					  (e: "removeDocument"): void;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const has_existing_doc = computed<boolean>(() => {
 | 
					const has_existing_doc = computed<boolean>(() => {
 | 
				
			||||||
    return props.existingDoc !== undefined && props.existingDoc !== null;
 | 
					  return props.existingDoc !== undefined && props.existingDoc !== null;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dav_link_expiration = computed<number | undefined>(() => {
 | 
					const dav_link_expiration = computed<number | undefined>(() => {
 | 
				
			||||||
    if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
					  if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
				
			||||||
        return undefined;
 | 
					    return undefined;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (props.existingDoc.status !== "ready") {
 | 
					  if (props.existingDoc.status !== "ready") {
 | 
				
			||||||
        return undefined;
 | 
					    return undefined;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return props.existingDoc._links?.dav_link?.expiration;
 | 
					  return props.existingDoc._links?.dav_link?.expiration;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dav_link_href = computed<string | undefined>(() => {
 | 
					const dav_link_href = computed<string | undefined>(() => {
 | 
				
			||||||
    if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
					  if (props.existingDoc === undefined || props.existingDoc === null) {
 | 
				
			||||||
        return undefined;
 | 
					    return undefined;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if (props.existingDoc.status !== "ready") {
 | 
					  if (props.existingDoc.status !== "ready") {
 | 
				
			||||||
        return undefined;
 | 
					    return undefined;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return props.existingDoc._links?.dav_link?.href;
 | 
					  return props.existingDoc._links?.dav_link?.href;
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onAddDocument = ({
 | 
					const onAddDocument = ({
 | 
				
			||||||
    stored_object,
 | 
					  stored_object,
 | 
				
			||||||
    stored_object_version,
 | 
					  stored_object_version,
 | 
				
			||||||
    file_name,
 | 
					  file_name,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    stored_object: StoredObject;
 | 
					  stored_object: StoredObject;
 | 
				
			||||||
    stored_object_version: StoredObjectVersion;
 | 
					  stored_object_version: StoredObjectVersion;
 | 
				
			||||||
    file_name: string;
 | 
					  file_name: string;
 | 
				
			||||||
}): void => {
 | 
					}): void => {
 | 
				
			||||||
    emit("addDocument", { stored_object, stored_object_version, file_name });
 | 
					  emit("addDocument", { stored_object, stored_object_version, file_name });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onRemoveDocument = (e: Event): void => {
 | 
					const onRemoveDocument = (e: Event): void => {
 | 
				
			||||||
    e.stopPropagation();
 | 
					  e.stopPropagation();
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
    emit("removeDocument");
 | 
					  emit("removeDocument");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div>
 | 
					  <div>
 | 
				
			||||||
        <drop-file
 | 
					    <drop-file
 | 
				
			||||||
            :existingDoc="props.existingDoc"
 | 
					      :existingDoc="props.existingDoc"
 | 
				
			||||||
            @addDocument="onAddDocument"
 | 
					      @addDocument="onAddDocument"
 | 
				
			||||||
        ></drop-file>
 | 
					    ></drop-file>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ul class="record_actions">
 | 
					    <ul class="record_actions">
 | 
				
			||||||
            <li v-if="has_existing_doc">
 | 
					      <li v-if="has_existing_doc">
 | 
				
			||||||
                <document-action-buttons-group
 | 
					        <document-action-buttons-group
 | 
				
			||||||
                    :stored-object="props.existingDoc"
 | 
					          :stored-object="props.existingDoc"
 | 
				
			||||||
                    :can-edit="props.existingDoc?.status === 'ready'"
 | 
					          :can-edit="props.existingDoc?.status === 'ready'"
 | 
				
			||||||
                    :can-download="true"
 | 
					          :can-download="true"
 | 
				
			||||||
                    :dav-link="dav_link_href"
 | 
					          :dav-link="dav_link_href"
 | 
				
			||||||
                    :dav-link-expiration="dav_link_expiration"
 | 
					          :dav-link-expiration="dav_link_expiration"
 | 
				
			||||||
                />
 | 
					        />
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
            <li>
 | 
					      <li>
 | 
				
			||||||
                <button
 | 
					        <button
 | 
				
			||||||
                    v-if="allowRemove"
 | 
					          v-if="allowRemove"
 | 
				
			||||||
                    class="btn btn-delete"
 | 
					          class="btn btn-delete"
 | 
				
			||||||
                    @click="onRemoveDocument($event)"
 | 
					          @click="onRemoveDocument($event)"
 | 
				
			||||||
                ></button>
 | 
					        ></button>
 | 
				
			||||||
            </li>
 | 
					      </li>
 | 
				
			||||||
        </ul>
 | 
					    </ul>
 | 
				
			||||||
    </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,46 +1,46 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
interface FileIconConfig {
 | 
					interface FileIconConfig {
 | 
				
			||||||
    type: string;
 | 
					  type: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<FileIconConfig>();
 | 
					const props = defineProps<FileIconConfig>();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <i class="fa fa-file-pdf-o" v-if="props.type === 'application/pdf'"></i>
 | 
					  <i class="fa fa-file-pdf-o" v-if="props.type === 'application/pdf'"></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-word-o"
 | 
					    class="fa fa-file-word-o"
 | 
				
			||||||
        v-else-if="props.type === 'application/vnd.oasis.opendocument.text'"
 | 
					    v-else-if="props.type === 'application/vnd.oasis.opendocument.text'"
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-word-o"
 | 
					    class="fa fa-file-word-o"
 | 
				
			||||||
        v-else-if="
 | 
					    v-else-if="
 | 
				
			||||||
            props.type ===
 | 
					      props.type ===
 | 
				
			||||||
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
 | 
					      'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
 | 
				
			||||||
        "
 | 
					    "
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-word-o"
 | 
					    class="fa fa-file-word-o"
 | 
				
			||||||
        v-else-if="props.type === 'application/msword'"
 | 
					    v-else-if="props.type === 'application/msword'"
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-excel-o"
 | 
					    class="fa fa-file-excel-o"
 | 
				
			||||||
        v-else-if="
 | 
					    v-else-if="
 | 
				
			||||||
            props.type ===
 | 
					      props.type ===
 | 
				
			||||||
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
 | 
					      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
 | 
				
			||||||
        "
 | 
					    "
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-excel-o"
 | 
					    class="fa fa-file-excel-o"
 | 
				
			||||||
        v-else-if="props.type === 'application/vnd.ms-excel'"
 | 
					    v-else-if="props.type === 'application/vnd.ms-excel'"
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i class="fa fa-file-image-o" v-else-if="props.type === 'image/jpeg'"></i>
 | 
					  <i class="fa fa-file-image-o" v-else-if="props.type === 'image/jpeg'"></i>
 | 
				
			||||||
    <i class="fa fa-file-image-o" v-else-if="props.type === 'image/png'"></i>
 | 
					  <i class="fa fa-file-image-o" v-else-if="props.type === 'image/png'"></i>
 | 
				
			||||||
    <i
 | 
					  <i
 | 
				
			||||||
        class="fa fa-file-archive-o"
 | 
					    class="fa fa-file-archive-o"
 | 
				
			||||||
        v-else-if="props.type === 'application/x-zip-compressed'"
 | 
					    v-else-if="props.type === 'application/x-zip-compressed'"
 | 
				
			||||||
    ></i>
 | 
					  ></i>
 | 
				
			||||||
    <i class="fa fa-file-code-o" v-else></i>
 | 
					  <i class="fa fa-file-code-o" v-else></i>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +1,28 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <a :class="props.classes" @click="download_and_open($event)" ref="btn">
 | 
					  <a :class="props.classes" @click="download_and_open($event)" ref="btn">
 | 
				
			||||||
        <i class="fa fa-file-pdf-o"></i>
 | 
					    <i class="fa fa-file-pdf-o"></i>
 | 
				
			||||||
        Télécharger en pdf
 | 
					    Télécharger en pdf
 | 
				
			||||||
    </a>
 | 
					  </a>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    build_convert_link,
 | 
					  build_convert_link,
 | 
				
			||||||
    download_and_decrypt_doc,
 | 
					  download_and_decrypt_doc,
 | 
				
			||||||
    download_doc,
 | 
					  download_doc,
 | 
				
			||||||
} from "./helpers";
 | 
					} from "./helpers";
 | 
				
			||||||
import mime from "mime";
 | 
					import mime from "mime";
 | 
				
			||||||
import { reactive, ref } from "vue";
 | 
					import { reactive, ref } from "vue";
 | 
				
			||||||
import { StoredObject } from "../../types";
 | 
					import { StoredObject } from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ConvertButtonConfig {
 | 
					interface ConvertButtonConfig {
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    classes: Record<string, boolean>;
 | 
					  classes: Record<string, boolean>;
 | 
				
			||||||
    filename?: string;
 | 
					  filename?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DownloadButtonState {
 | 
					interface DownloadButtonState {
 | 
				
			||||||
    content: null | string;
 | 
					  content: null | string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<ConvertButtonConfig>();
 | 
					const props = defineProps<ConvertButtonConfig>();
 | 
				
			||||||
@@ -30,36 +30,34 @@ const state: DownloadButtonState = reactive({ content: null });
 | 
				
			|||||||
const btn = ref<HTMLAnchorElement | null>(null);
 | 
					const btn = ref<HTMLAnchorElement | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function download_and_open(event: Event): Promise<void> {
 | 
					async function download_and_open(event: Event): Promise<void> {
 | 
				
			||||||
    const button = event.target as HTMLAnchorElement;
 | 
					  const button = event.target as HTMLAnchorElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null === state.content) {
 | 
					  if (null === state.content) {
 | 
				
			||||||
        event.preventDefault();
 | 
					    event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const raw = await download_doc(
 | 
					    const raw = await download_doc(build_convert_link(props.storedObject.uuid));
 | 
				
			||||||
            build_convert_link(props.storedObject.uuid),
 | 
					    state.content = window.URL.createObjectURL(raw);
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        state.content = window.URL.createObjectURL(raw);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        button.href = window.URL.createObjectURL(raw);
 | 
					    button.href = window.URL.createObjectURL(raw);
 | 
				
			||||||
        button.type = "application/pdf";
 | 
					    button.type = "application/pdf";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        button.download = props.filename + ".pdf" || "document.pdf";
 | 
					    button.download = props.filename + ".pdf" || "document.pdf";
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    button.click();
 | 
					  button.click();
 | 
				
			||||||
    const reset_pending = setTimeout(reset_state, 45000);
 | 
					  const reset_pending = setTimeout(reset_state, 45000);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function reset_state(): void {
 | 
					function reset_state(): void {
 | 
				
			||||||
    state.content = null;
 | 
					  state.content = null;
 | 
				
			||||||
    btn.value?.removeAttribute("download");
 | 
					  btn.value?.removeAttribute("download");
 | 
				
			||||||
    btn.value?.removeAttribute("href");
 | 
					  btn.value?.removeAttribute("href");
 | 
				
			||||||
    btn.value?.removeAttribute("type");
 | 
					  btn.value?.removeAttribute("type");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
i.fa::before {
 | 
					i.fa::before {
 | 
				
			||||||
    color: var(--bs-dropdown-link-hover-color);
 | 
					  color: var(--bs-dropdown-link-hover-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,13 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
 | 
				
			|||||||
import { computed, reactive } from "vue";
 | 
					import { computed, reactive } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface DesktopEditButtonConfig {
 | 
					export interface DesktopEditButtonConfig {
 | 
				
			||||||
    editLink: null;
 | 
					  editLink: null;
 | 
				
			||||||
    classes: Record<string, boolean>;
 | 
					  classes: Record<string, boolean>;
 | 
				
			||||||
    expirationLink: number | Date;
 | 
					  expirationLink: number | Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DesktopEditButtonState {
 | 
					interface DesktopEditButtonState {
 | 
				
			||||||
    modalOpened: boolean;
 | 
					  modalOpened: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const state: DesktopEditButtonState = reactive({ modalOpened: false });
 | 
					const state: DesktopEditButtonState = reactive({ modalOpened: false });
 | 
				
			||||||
@@ -17,80 +17,76 @@ const state: DesktopEditButtonState = reactive({ modalOpened: false });
 | 
				
			|||||||
const props = defineProps<DesktopEditButtonConfig>();
 | 
					const props = defineProps<DesktopEditButtonConfig>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const buildCommand = computed<string>(
 | 
					const buildCommand = computed<string>(
 | 
				
			||||||
    () => "vnd.libreoffice.command:ofe|u|" + props.editLink,
 | 
					  () => "vnd.libreoffice.command:ofe|u|" + props.editLink,
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const editionUntilFormatted = computed<string>(() => {
 | 
					const editionUntilFormatted = computed<string>(() => {
 | 
				
			||||||
    let d;
 | 
					  let d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (props.expirationLink instanceof Date) {
 | 
					  if (props.expirationLink instanceof Date) {
 | 
				
			||||||
        d = props.expirationLink;
 | 
					    d = props.expirationLink;
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        d = new Date(props.expirationLink * 1000);
 | 
					    d = new Date(props.expirationLink * 1000);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    console.log(props.expirationLink);
 | 
					  console.log(props.expirationLink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new Intl.DateTimeFormat(undefined, {
 | 
					  return new Intl.DateTimeFormat(undefined, {
 | 
				
			||||||
        dateStyle: "long",
 | 
					    dateStyle: "long",
 | 
				
			||||||
        timeStyle: "medium",
 | 
					    timeStyle: "medium",
 | 
				
			||||||
    }).format(d);
 | 
					  }).format(d);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <teleport to="body">
 | 
					  <teleport to="body">
 | 
				
			||||||
        <modal v-if="state.modalOpened" @close="state.modalOpened = false">
 | 
					    <modal v-if="state.modalOpened" @close="state.modalOpened = false">
 | 
				
			||||||
            <template v-slot:body>
 | 
					      <template v-slot:body>
 | 
				
			||||||
                <div class="desktop-edit">
 | 
					        <div class="desktop-edit">
 | 
				
			||||||
                    <p class="center">
 | 
					          <p class="center">Veuillez enregistrer vos modifications avant le</p>
 | 
				
			||||||
                        Veuillez enregistrer vos modifications avant le
 | 
					          <p>
 | 
				
			||||||
                    </p>
 | 
					            <strong>{{ editionUntilFormatted }}</strong>
 | 
				
			||||||
                    <p>
 | 
					          </p>
 | 
				
			||||||
                        <strong>{{ editionUntilFormatted }}</strong>
 | 
					 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <p>
 | 
					          <p>
 | 
				
			||||||
                        <a class="btn btn-primary" :href="buildCommand"
 | 
					            <a class="btn btn-primary" :href="buildCommand"
 | 
				
			||||||
                            >Ouvrir le document pour édition</a
 | 
					              >Ouvrir le document pour édition</a
 | 
				
			||||||
                        >
 | 
					            >
 | 
				
			||||||
                    </p>
 | 
					          </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <p>
 | 
					          <p>
 | 
				
			||||||
                        <small
 | 
					            <small
 | 
				
			||||||
                            >Le document peut être édité uniquement en utilisant
 | 
					              >Le document peut être édité uniquement en utilisant Libre
 | 
				
			||||||
                            Libre Office.</small
 | 
					              Office.</small
 | 
				
			||||||
                        >
 | 
					            >
 | 
				
			||||||
                    </p>
 | 
					          </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <p>
 | 
					          <p>
 | 
				
			||||||
                        <small
 | 
					            <small
 | 
				
			||||||
                            >En cas d'échec lors de l'enregistrement, sauver le
 | 
					              >En cas d'échec lors de l'enregistrement, sauver le document sur
 | 
				
			||||||
                            document sur le poste de travail avant de le déposer
 | 
					              le poste de travail avant de le déposer à nouveau ici.</small
 | 
				
			||||||
                            à nouveau ici.</small
 | 
					            >
 | 
				
			||||||
                        >
 | 
					          </p>
 | 
				
			||||||
                    </p>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <p>
 | 
					          <p>
 | 
				
			||||||
                        <small
 | 
					            <small
 | 
				
			||||||
                            >Vous pouvez naviguez sur d'autres pages pendant
 | 
					              >Vous pouvez naviguez sur d'autres pages pendant l'édition.</small
 | 
				
			||||||
                            l'édition.</small
 | 
					            >
 | 
				
			||||||
                        >
 | 
					          </p>
 | 
				
			||||||
                    </p>
 | 
					        </div>
 | 
				
			||||||
                </div>
 | 
					      </template>
 | 
				
			||||||
            </template>
 | 
					    </modal>
 | 
				
			||||||
        </modal>
 | 
					  </teleport>
 | 
				
			||||||
    </teleport>
 | 
					  <a :class="props.classes" @click="state.modalOpened = true">
 | 
				
			||||||
    <a :class="props.classes" @click="state.modalOpened = true">
 | 
					    <i class="fa fa-desktop"></i>
 | 
				
			||||||
        <i class="fa fa-desktop"></i>
 | 
					    Éditer sur le bureau
 | 
				
			||||||
        Éditer sur le bureau
 | 
					  </a>
 | 
				
			||||||
    </a>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
.desktop-edit {
 | 
					.desktop-edit {
 | 
				
			||||||
    text-align: center;
 | 
					  text-align: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
i.fa::before {
 | 
					i.fa::before {
 | 
				
			||||||
    color: var(--bs-dropdown-link-hover-color);
 | 
					  color: var(--bs-dropdown-link-hover-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,26 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <a
 | 
					  <a
 | 
				
			||||||
        v-if="!state.is_ready"
 | 
					    v-if="!state.is_ready"
 | 
				
			||||||
        :class="props.classes"
 | 
					    :class="props.classes"
 | 
				
			||||||
        @click="download_and_open()"
 | 
					    @click="download_and_open()"
 | 
				
			||||||
        title="Télécharger"
 | 
					    title="Télécharger"
 | 
				
			||||||
    >
 | 
					  >
 | 
				
			||||||
        <i class="fa fa-download"></i>
 | 
					    <i class="fa fa-download"></i>
 | 
				
			||||||
        <template v-if="displayActionStringInButton">Télécharger</template>
 | 
					    <template v-if="displayActionStringInButton">Télécharger</template>
 | 
				
			||||||
    </a>
 | 
					  </a>
 | 
				
			||||||
    <a
 | 
					  <a
 | 
				
			||||||
        v-else
 | 
					    v-else
 | 
				
			||||||
        :class="props.classes"
 | 
					    :class="props.classes"
 | 
				
			||||||
        target="_blank"
 | 
					    target="_blank"
 | 
				
			||||||
        :type="props.atVersion.type"
 | 
					    :type="props.atVersion.type"
 | 
				
			||||||
        :download="buildDocumentName()"
 | 
					    :download="buildDocumentName()"
 | 
				
			||||||
        :href="state.href_url"
 | 
					    :href="state.href_url"
 | 
				
			||||||
        ref="open_button"
 | 
					    ref="open_button"
 | 
				
			||||||
        title="Ouvrir"
 | 
					    title="Ouvrir"
 | 
				
			||||||
    >
 | 
					  >
 | 
				
			||||||
        <i class="fa fa-external-link"></i>
 | 
					    <i class="fa fa-external-link"></i>
 | 
				
			||||||
        <template v-if="displayActionStringInButton">Ouvrir</template>
 | 
					    <template v-if="displayActionStringInButton">Ouvrir</template>
 | 
				
			||||||
    </a>
 | 
					  </a>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
@@ -30,112 +30,109 @@ import mime from "mime";
 | 
				
			|||||||
import { StoredObject, StoredObjectVersion } from "../../types";
 | 
					import { StoredObject, StoredObjectVersion } from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DownloadButtonConfig {
 | 
					interface DownloadButtonConfig {
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    atVersion: StoredObjectVersion;
 | 
					  atVersion: StoredObjectVersion;
 | 
				
			||||||
    classes: Record<string, boolean>;
 | 
					  classes: Record<string, boolean>;
 | 
				
			||||||
    filename?: string;
 | 
					  filename?: string;
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * if true, display the action string into the button. If false, displays only
 | 
					   * if true, display the action string into the button. If false, displays only
 | 
				
			||||||
     * the icon
 | 
					   * the icon
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    displayActionStringInButton?: boolean;
 | 
					  displayActionStringInButton?: boolean;
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * if true, will download directly the file on load
 | 
					   * if true, will download directly the file on load
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    directDownload?: boolean;
 | 
					  directDownload?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface DownloadButtonState {
 | 
					interface DownloadButtonState {
 | 
				
			||||||
    is_ready: boolean;
 | 
					  is_ready: boolean;
 | 
				
			||||||
    is_running: boolean;
 | 
					  is_running: boolean;
 | 
				
			||||||
    href_url: string;
 | 
					  href_url: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = withDefaults(defineProps<DownloadButtonConfig>(), {
 | 
					const props = withDefaults(defineProps<DownloadButtonConfig>(), {
 | 
				
			||||||
    displayActionStringInButton: true,
 | 
					  displayActionStringInButton: true,
 | 
				
			||||||
    directDownload: false,
 | 
					  directDownload: false,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
const state: DownloadButtonState = reactive({
 | 
					const state: DownloadButtonState = reactive({
 | 
				
			||||||
    is_ready: false,
 | 
					  is_ready: false,
 | 
				
			||||||
    is_running: false,
 | 
					  is_running: false,
 | 
				
			||||||
    href_url: "#",
 | 
					  href_url: "#",
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const open_button = ref<HTMLAnchorElement | null>(null);
 | 
					const open_button = ref<HTMLAnchorElement | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function buildDocumentName(): string {
 | 
					function buildDocumentName(): string {
 | 
				
			||||||
    let document_name = props.filename ?? props.storedObject.title;
 | 
					  let document_name = props.filename ?? props.storedObject.title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ("" === document_name || null === document_name) {
 | 
					  if ("" === document_name || null === document_name) {
 | 
				
			||||||
        document_name = "document";
 | 
					    document_name = "document";
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const ext = mime.getExtension(props.atVersion.type);
 | 
					  const ext = mime.getExtension(props.atVersion.type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null !== ext) {
 | 
					  if (null !== ext) {
 | 
				
			||||||
        return document_name + "." + ext;
 | 
					    return document_name + "." + ext;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return document_name;
 | 
					  return document_name;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function download_and_open(): Promise<void> {
 | 
					async function download_and_open(): Promise<void> {
 | 
				
			||||||
    if (state.is_running) {
 | 
					  if (state.is_running) {
 | 
				
			||||||
        console.log("state is running, aborting");
 | 
					    console.log("state is running, aborting");
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state.is_running = true;
 | 
					  state.is_running = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (state.is_ready) {
 | 
					  if (state.is_ready) {
 | 
				
			||||||
        console.log("state is ready. This should not happens");
 | 
					    console.log("state is ready. This should not happens");
 | 
				
			||||||
        return;
 | 
					    return;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let raw;
 | 
					  let raw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					  try {
 | 
				
			||||||
        raw = await download_and_decrypt_doc(
 | 
					    raw = await download_and_decrypt_doc(props.storedObject, props.atVersion);
 | 
				
			||||||
            props.storedObject,
 | 
					  } catch (e) {
 | 
				
			||||||
            props.atVersion,
 | 
					    console.error("error while downloading and decrypting document");
 | 
				
			||||||
        );
 | 
					    console.error(e);
 | 
				
			||||||
    } catch (e) {
 | 
					    throw e;
 | 
				
			||||||
        console.error("error while downloading and decrypting document");
 | 
					  }
 | 
				
			||||||
        console.error(e);
 | 
					 | 
				
			||||||
        throw e;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state.href_url = window.URL.createObjectURL(raw);
 | 
					  state.href_url = window.URL.createObjectURL(raw);
 | 
				
			||||||
    state.is_running = false;
 | 
					  state.is_running = false;
 | 
				
			||||||
    state.is_ready = true;
 | 
					  state.is_ready = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!props.directDownload) {
 | 
					  if (!props.directDownload) {
 | 
				
			||||||
        await nextTick();
 | 
					    await nextTick();
 | 
				
			||||||
        open_button.value?.click();
 | 
					    open_button.value?.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        console.log("open button should have been clicked");
 | 
					    console.log("open button should have been clicked");
 | 
				
			||||||
        setTimeout(reset_state, 45000);
 | 
					    setTimeout(reset_state, 45000);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function reset_state(): void {
 | 
					function reset_state(): void {
 | 
				
			||||||
    state.href_url = "#";
 | 
					  state.href_url = "#";
 | 
				
			||||||
    state.is_ready = false;
 | 
					  state.is_ready = false;
 | 
				
			||||||
    state.is_running = false;
 | 
					  state.is_running = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
    if (props.directDownload) {
 | 
					  if (props.directDownload) {
 | 
				
			||||||
        download_and_open();
 | 
					    download_and_open();
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
i.fa::before {
 | 
					i.fa::before {
 | 
				
			||||||
    color: var(--bs-dropdown-link-hover-color);
 | 
					  color: var(--bs-dropdown-link-hover-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
i.fa {
 | 
					i.fa {
 | 
				
			||||||
    margin-right: 0.5rem;
 | 
					  margin-right: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,20 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import HistoryButtonModal from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue";
 | 
					import HistoryButtonModal from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonModal.vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "./../../types";
 | 
					} from "./../../types";
 | 
				
			||||||
import { computed, reactive, ref, useTemplateRef } from "vue";
 | 
					import { computed, reactive, ref, useTemplateRef } from "vue";
 | 
				
			||||||
import { get_versions } from "./HistoryButton/api";
 | 
					import { get_versions } from "./HistoryButton/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonConfig {
 | 
					interface HistoryButtonConfig {
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    canEdit: boolean;
 | 
					  canEdit: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonState {
 | 
					interface HistoryButtonState {
 | 
				
			||||||
    versions: StoredObjectVersionWithPointInTime[];
 | 
					  versions: StoredObjectVersionWithPointInTime[];
 | 
				
			||||||
    loaded: boolean;
 | 
					  loaded: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<HistoryButtonConfig>();
 | 
					const props = defineProps<HistoryButtonConfig>();
 | 
				
			||||||
@@ -22,47 +22,47 @@ const state = reactive<HistoryButtonState>({ versions: [], loaded: false });
 | 
				
			|||||||
const modal = useTemplateRef<typeof HistoryButtonModal>("modal");
 | 
					const modal = useTemplateRef<typeof HistoryButtonModal>("modal");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const download_version_and_open_modal = async function (): Promise<void> {
 | 
					const download_version_and_open_modal = async function (): Promise<void> {
 | 
				
			||||||
    if (null !== modal.value) {
 | 
					  if (null !== modal.value) {
 | 
				
			||||||
        modal.value.open();
 | 
					    modal.value.open();
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        console.log("modal is null");
 | 
					    console.log("modal is null");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!state.loaded) {
 | 
					  if (!state.loaded) {
 | 
				
			||||||
        const versions = await get_versions(props.storedObject);
 | 
					    const versions = await get_versions(props.storedObject);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const version of versions) {
 | 
					    for (const version of versions) {
 | 
				
			||||||
            state.versions.push(version);
 | 
					      state.versions.push(version);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        state.loaded = true;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    state.loaded = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onRestoreVersion = ({
 | 
					const onRestoreVersion = ({
 | 
				
			||||||
    newVersion,
 | 
					  newVersion,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    newVersion: StoredObjectVersionWithPointInTime;
 | 
					  newVersion: StoredObjectVersionWithPointInTime;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    state.versions.unshift(newVersion);
 | 
					  state.versions.unshift(newVersion);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <a @click="download_version_and_open_modal" class="dropdown-item">
 | 
					  <a @click="download_version_and_open_modal" class="dropdown-item">
 | 
				
			||||||
        <history-button-modal
 | 
					    <history-button-modal
 | 
				
			||||||
            ref="modal"
 | 
					      ref="modal"
 | 
				
			||||||
            :versions="state.versions"
 | 
					      :versions="state.versions"
 | 
				
			||||||
            :stored-object="storedObject"
 | 
					      :stored-object="storedObject"
 | 
				
			||||||
            :can-edit="canEdit"
 | 
					      :can-edit="canEdit"
 | 
				
			||||||
            @restore-version="onRestoreVersion"
 | 
					      @restore-version="onRestoreVersion"
 | 
				
			||||||
        ></history-button-modal>
 | 
					    ></history-button-modal>
 | 
				
			||||||
        <i class="fa fa-history"></i>
 | 
					    <i class="fa fa-history"></i>
 | 
				
			||||||
        Historique
 | 
					    Historique
 | 
				
			||||||
    </a>
 | 
					  </a>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
i.fa::before {
 | 
					i.fa::before {
 | 
				
			||||||
    color: var(--bs-dropdown-link-hover-color);
 | 
					  color: var(--bs-dropdown-link-hover-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,26 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "./../../../types";
 | 
					} from "./../../../types";
 | 
				
			||||||
import HistoryButtonListItem from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue";
 | 
					import HistoryButtonListItem from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonListItem.vue";
 | 
				
			||||||
import { computed, reactive } from "vue";
 | 
					import { computed, reactive } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonListConfig {
 | 
					interface HistoryButtonListConfig {
 | 
				
			||||||
    versions: StoredObjectVersionWithPointInTime[];
 | 
					  versions: StoredObjectVersionWithPointInTime[];
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    canEdit: boolean;
 | 
					  canEdit: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
					  restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonListState {
 | 
					interface HistoryButtonListState {
 | 
				
			||||||
    /**
 | 
					  /**
 | 
				
			||||||
     * Contains the number of the newly created version when a version is restored.
 | 
					   * Contains the number of the newly created version when a version is restored.
 | 
				
			||||||
     */
 | 
					   */
 | 
				
			||||||
    restored: number;
 | 
					  restored: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<HistoryButtonListConfig>();
 | 
					const props = defineProps<HistoryButtonListConfig>();
 | 
				
			||||||
@@ -28,11 +28,11 @@ const props = defineProps<HistoryButtonListConfig>();
 | 
				
			|||||||
const state = reactive<HistoryButtonListState>({ restored: -1 });
 | 
					const state = reactive<HistoryButtonListState>({ restored: -1 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const higher_version = computed<number>(() =>
 | 
					const higher_version = computed<number>(() =>
 | 
				
			||||||
    props.versions.reduce(
 | 
					  props.versions.reduce(
 | 
				
			||||||
        (accumulator: number, version: StoredObjectVersionWithPointInTime) =>
 | 
					    (accumulator: number, version: StoredObjectVersionWithPointInTime) =>
 | 
				
			||||||
            Math.max(accumulator, version.version),
 | 
					      Math.max(accumulator, version.version),
 | 
				
			||||||
        -1,
 | 
					    -1,
 | 
				
			||||||
    ),
 | 
					  ),
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -41,32 +41,32 @@ const higher_version = computed<number>(() =>
 | 
				
			|||||||
 * internally, keep track of the newly restored version
 | 
					 * internally, keep track of the newly restored version
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const onRestored = ({
 | 
					const onRestored = ({
 | 
				
			||||||
    newVersion,
 | 
					  newVersion,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    newVersion: StoredObjectVersionWithPointInTime;
 | 
					  newVersion: StoredObjectVersionWithPointInTime;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    state.restored = newVersion.version;
 | 
					  state.restored = newVersion.version;
 | 
				
			||||||
    emit("restoreVersion", { newVersion });
 | 
					  emit("restoreVersion", { newVersion });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <template v-if="props.versions.length > 0">
 | 
					  <template v-if="props.versions.length > 0">
 | 
				
			||||||
        <div class="container">
 | 
					    <div class="container">
 | 
				
			||||||
            <template v-for="v in props.versions" :key="v.id">
 | 
					      <template v-for="v in props.versions" :key="v.id">
 | 
				
			||||||
                <history-button-list-item
 | 
					        <history-button-list-item
 | 
				
			||||||
                    :version="v"
 | 
					          :version="v"
 | 
				
			||||||
                    :can-edit="canEdit"
 | 
					          :can-edit="canEdit"
 | 
				
			||||||
                    :is-current="higher_version === v.version"
 | 
					          :is-current="higher_version === v.version"
 | 
				
			||||||
                    :stored-object="storedObject"
 | 
					          :stored-object="storedObject"
 | 
				
			||||||
                    @restore-version="onRestored"
 | 
					          @restore-version="onRestored"
 | 
				
			||||||
                ></history-button-list-item>
 | 
					        ></history-button-list-item>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
        </div>
 | 
					    </div>
 | 
				
			||||||
    </template>
 | 
					  </template>
 | 
				
			||||||
    <template v-else>
 | 
					  <template v-else>
 | 
				
			||||||
        <p>Chargement des versions</p>
 | 
					    <p>Chargement des versions</p>
 | 
				
			||||||
    </template>
 | 
					  </template>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectPointInTime,
 | 
					  StoredObjectPointInTime,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "./../../../types";
 | 
					} from "./../../../types";
 | 
				
			||||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
 | 
					import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
 | 
				
			||||||
import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
					import { ISOToDatetime } from "./../../../../../../ChillMainBundle/Resources/public/chill/js/date";
 | 
				
			||||||
@@ -12,185 +12,173 @@ import DownloadButton from "ChillDocStoreAssets/vuejs/StoredObjectButton/Downloa
 | 
				
			|||||||
import { computed } from "vue";
 | 
					import { computed } from "vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonListItemConfig {
 | 
					interface HistoryButtonListItemConfig {
 | 
				
			||||||
    version: StoredObjectVersionWithPointInTime;
 | 
					  version: StoredObjectVersionWithPointInTime;
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    canEdit: boolean;
 | 
					  canEdit: boolean;
 | 
				
			||||||
    isCurrent: boolean;
 | 
					  isCurrent: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
					  restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<HistoryButtonListItemConfig>();
 | 
					const props = defineProps<HistoryButtonListItemConfig>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onRestore = ({
 | 
					const onRestore = ({
 | 
				
			||||||
    newVersion,
 | 
					  newVersion,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
    newVersion: StoredObjectVersionWithPointInTime;
 | 
					  newVersion: StoredObjectVersionWithPointInTime;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    emit("restoreVersion", { newVersion });
 | 
					  emit("restoreVersion", { newVersion });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isKeptBeforeConversion = computed<boolean>(() => {
 | 
					const isKeptBeforeConversion = computed<boolean>(() => {
 | 
				
			||||||
    if ("point-in-times" in props.version) {
 | 
					  if ("point-in-times" in props.version) {
 | 
				
			||||||
        return props.version["point-in-times"].reduce(
 | 
					    return props.version["point-in-times"].reduce(
 | 
				
			||||||
            (accumulator: boolean, pit: StoredObjectPointInTime) =>
 | 
					      (accumulator: boolean, pit: StoredObjectPointInTime) =>
 | 
				
			||||||
                accumulator || "keep-before-conversion" === pit.reason,
 | 
					        accumulator || "keep-before-conversion" === pit.reason,
 | 
				
			||||||
            false,
 | 
					      false,
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        return false;
 | 
					    return false;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isRestored = computed<boolean>(
 | 
					const isRestored = computed<boolean>(
 | 
				
			||||||
    () => props.version.version > 0 && null !== props.version["from-restored"],
 | 
					  () => props.version.version > 0 && null !== props.version["from-restored"],
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isDuplicated = computed<boolean>(
 | 
					const isDuplicated = computed<boolean>(
 | 
				
			||||||
    () =>
 | 
					  () => props.version.version === 0 && null !== props.version["from-restored"],
 | 
				
			||||||
        props.version.version === 0 && null !== props.version["from-restored"],
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const classes = computed<{
 | 
					const classes = computed<{
 | 
				
			||||||
    row: true;
 | 
					  row: true;
 | 
				
			||||||
    "row-hover": true;
 | 
					  "row-hover": true;
 | 
				
			||||||
    "blinking-1": boolean;
 | 
					  "blinking-1": boolean;
 | 
				
			||||||
    "blinking-2": boolean;
 | 
					  "blinking-2": boolean;
 | 
				
			||||||
}>(() => ({
 | 
					}>(() => ({
 | 
				
			||||||
    row: true,
 | 
					  row: true,
 | 
				
			||||||
    "row-hover": true,
 | 
					  "row-hover": true,
 | 
				
			||||||
    "blinking-1": props.isRestored && 0 === props.version.version % 2,
 | 
					  "blinking-1": props.isRestored && 0 === props.version.version % 2,
 | 
				
			||||||
    "blinking-2": props.isRestored && 1 === props.version.version % 2,
 | 
					  "blinking-2": props.isRestored && 1 === props.version.version % 2,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div :class="classes">
 | 
					  <div :class="classes">
 | 
				
			||||||
        <div
 | 
					    <div
 | 
				
			||||||
            class="col-12 tags"
 | 
					      class="col-12 tags"
 | 
				
			||||||
            v-if="
 | 
					      v-if="isCurrent || isKeptBeforeConversion || isRestored || isDuplicated"
 | 
				
			||||||
                isCurrent ||
 | 
					    >
 | 
				
			||||||
                isKeptBeforeConversion ||
 | 
					      <span class="badge bg-success" v-if="isCurrent">Version actuelle</span>
 | 
				
			||||||
                isRestored ||
 | 
					      <span class="badge bg-info" v-if="isKeptBeforeConversion"
 | 
				
			||||||
                isDuplicated
 | 
					        >Conservée avant conversion dans un autre format</span
 | 
				
			||||||
            "
 | 
					      >
 | 
				
			||||||
        >
 | 
					      <span class="badge bg-info" v-if="isRestored"
 | 
				
			||||||
            <span class="badge bg-success" v-if="isCurrent"
 | 
					        >Restaurée depuis la version
 | 
				
			||||||
                >Version actuelle</span
 | 
					        {{ version["from-restored"]?.version + 1 }}</span
 | 
				
			||||||
            >
 | 
					      >
 | 
				
			||||||
            <span class="badge bg-info" v-if="isKeptBeforeConversion"
 | 
					      <span class="badge bg-info" v-if="isDuplicated"
 | 
				
			||||||
                >Conservée avant conversion dans un autre format</span
 | 
					        >Dupliqué depuis un autre document</span
 | 
				
			||||||
            >
 | 
					      >
 | 
				
			||||||
            <span class="badge bg-info" v-if="isRestored"
 | 
					 | 
				
			||||||
                >Restaurée depuis la version
 | 
					 | 
				
			||||||
                {{ version["from-restored"]?.version + 1 }}</span
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
            <span class="badge bg-info" v-if="isDuplicated"
 | 
					 | 
				
			||||||
                >Dupliqué depuis un autre document</span
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="col-12">
 | 
					 | 
				
			||||||
            <file-icon :type="version.type"></file-icon>
 | 
					 | 
				
			||||||
            <span
 | 
					 | 
				
			||||||
                ><strong> #{{ version.version + 1 }} </strong></span
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
            <template
 | 
					 | 
				
			||||||
                v-if="version.createdBy !== null && version.createdAt !== null"
 | 
					 | 
				
			||||||
                ><strong v-if="version.version == 0">créé par</strong
 | 
					 | 
				
			||||||
                ><strong v-else>modifié par</strong>
 | 
					 | 
				
			||||||
                <span class="badge-user"
 | 
					 | 
				
			||||||
                    ><UserRenderBoxBadge
 | 
					 | 
				
			||||||
                        :user="version.createdBy"
 | 
					 | 
				
			||||||
                    ></UserRenderBoxBadge
 | 
					 | 
				
			||||||
                ></span>
 | 
					 | 
				
			||||||
                <strong>à</strong>
 | 
					 | 
				
			||||||
                {{
 | 
					 | 
				
			||||||
                    $d(ISOToDatetime(version.createdAt.datetime8601), "long")
 | 
					 | 
				
			||||||
                }}</template
 | 
					 | 
				
			||||||
            ><template
 | 
					 | 
				
			||||||
                v-if="version.createdBy === null && version.createdAt !== null"
 | 
					 | 
				
			||||||
                ><strong v-if="version.version == 0">Créé le</strong
 | 
					 | 
				
			||||||
                ><strong v-else>modifié le</strong>
 | 
					 | 
				
			||||||
                {{
 | 
					 | 
				
			||||||
                    $d(ISOToDatetime(version.createdAt.datetime8601), "long")
 | 
					 | 
				
			||||||
                }}</template
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div class="col-12">
 | 
					 | 
				
			||||||
            <ul class="record_actions small slim on-version-actions">
 | 
					 | 
				
			||||||
                <li v-if="canEdit && !isCurrent">
 | 
					 | 
				
			||||||
                    <restore-version-button
 | 
					 | 
				
			||||||
                        :stored-object-version="props.version"
 | 
					 | 
				
			||||||
                        @restore-version="onRestore"
 | 
					 | 
				
			||||||
                    ></restore-version-button>
 | 
					 | 
				
			||||||
                </li>
 | 
					 | 
				
			||||||
                <li>
 | 
					 | 
				
			||||||
                    <download-button
 | 
					 | 
				
			||||||
                        :stored-object="storedObject"
 | 
					 | 
				
			||||||
                        :at-version="version"
 | 
					 | 
				
			||||||
                        :classes="{
 | 
					 | 
				
			||||||
                            btn: true,
 | 
					 | 
				
			||||||
                            'btn-outline-primary': true,
 | 
					 | 
				
			||||||
                            'btn-sm': true,
 | 
					 | 
				
			||||||
                        }"
 | 
					 | 
				
			||||||
                        :display-action-string-in-button="false"
 | 
					 | 
				
			||||||
                    ></download-button>
 | 
					 | 
				
			||||||
                </li>
 | 
					 | 
				
			||||||
            </ul>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="col-12">
 | 
				
			||||||
 | 
					      <file-icon :type="version.type"></file-icon>
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        ><strong> #{{ version.version + 1 }} </strong></span
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					      <template v-if="version.createdBy !== null && version.createdAt !== null"
 | 
				
			||||||
 | 
					        ><strong v-if="version.version == 0">créé par</strong
 | 
				
			||||||
 | 
					        ><strong v-else>modifié par</strong>
 | 
				
			||||||
 | 
					        <span class="badge-user"
 | 
				
			||||||
 | 
					          ><UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge
 | 
				
			||||||
 | 
					        ></span>
 | 
				
			||||||
 | 
					        <strong>à</strong>
 | 
				
			||||||
 | 
					        {{
 | 
				
			||||||
 | 
					          $d(ISOToDatetime(version.createdAt.datetime8601), "long")
 | 
				
			||||||
 | 
					        }}</template
 | 
				
			||||||
 | 
					      ><template v-if="version.createdBy === null && version.createdAt !== null"
 | 
				
			||||||
 | 
					        ><strong v-if="version.version == 0">Créé le</strong
 | 
				
			||||||
 | 
					        ><strong v-else>modifié le</strong>
 | 
				
			||||||
 | 
					        {{
 | 
				
			||||||
 | 
					          $d(ISOToDatetime(version.createdAt.datetime8601), "long")
 | 
				
			||||||
 | 
					        }}</template
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="col-12">
 | 
				
			||||||
 | 
					      <ul class="record_actions small slim on-version-actions">
 | 
				
			||||||
 | 
					        <li v-if="canEdit && !isCurrent">
 | 
				
			||||||
 | 
					          <restore-version-button
 | 
				
			||||||
 | 
					            :stored-object-version="props.version"
 | 
				
			||||||
 | 
					            @restore-version="onRestore"
 | 
				
			||||||
 | 
					          ></restore-version-button>
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					        <li>
 | 
				
			||||||
 | 
					          <download-button
 | 
				
			||||||
 | 
					            :stored-object="storedObject"
 | 
				
			||||||
 | 
					            :at-version="version"
 | 
				
			||||||
 | 
					            :classes="{
 | 
				
			||||||
 | 
					              btn: true,
 | 
				
			||||||
 | 
					              'btn-outline-primary': true,
 | 
				
			||||||
 | 
					              'btn-sm': true,
 | 
				
			||||||
 | 
					            }"
 | 
				
			||||||
 | 
					            :display-action-string-in-button="false"
 | 
				
			||||||
 | 
					          ></download-button>
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
div.tags {
 | 
					div.tags {
 | 
				
			||||||
    span.badge:not(:last-child) {
 | 
					  span.badge:not(:last-child) {
 | 
				
			||||||
        margin-right: 0.5rem;
 | 
					    margin-right: 0.5rem;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// to make the animation restart, we have the same animation twice,
 | 
					// to make the animation restart, we have the same animation twice,
 | 
				
			||||||
// and alternate between both
 | 
					// and alternate between both
 | 
				
			||||||
.blinking-1 {
 | 
					.blinking-1 {
 | 
				
			||||||
    animation-name: backgroundColorPalette-1;
 | 
					  animation-name: backgroundColorPalette-1;
 | 
				
			||||||
    animation-duration: 8s;
 | 
					  animation-duration: 8s;
 | 
				
			||||||
    animation-iteration-count: 1;
 | 
					  animation-iteration-count: 1;
 | 
				
			||||||
    animation-direction: normal;
 | 
					  animation-direction: normal;
 | 
				
			||||||
    animation-timing-function: linear;
 | 
					  animation-timing-function: linear;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@keyframes backgroundColorPalette-1 {
 | 
					@keyframes backgroundColorPalette-1 {
 | 
				
			||||||
    0% {
 | 
					  0% {
 | 
				
			||||||
        background: var(--bs-chill-green-dark);
 | 
					    background: var(--bs-chill-green-dark);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    25% {
 | 
					  25% {
 | 
				
			||||||
        background: var(--bs-chill-green);
 | 
					    background: var(--bs-chill-green);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    65% {
 | 
					  65% {
 | 
				
			||||||
        background: var(--bs-chill-beige);
 | 
					    background: var(--bs-chill-beige);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    100% {
 | 
					  100% {
 | 
				
			||||||
        background: unset;
 | 
					    background: unset;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.blinking-2 {
 | 
					.blinking-2 {
 | 
				
			||||||
    animation-name: backgroundColorPalette-2;
 | 
					  animation-name: backgroundColorPalette-2;
 | 
				
			||||||
    animation-duration: 8s;
 | 
					  animation-duration: 8s;
 | 
				
			||||||
    animation-iteration-count: 1;
 | 
					  animation-iteration-count: 1;
 | 
				
			||||||
    animation-direction: normal;
 | 
					  animation-direction: normal;
 | 
				
			||||||
    animation-timing-function: linear;
 | 
					  animation-timing-function: linear;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@keyframes backgroundColorPalette-2 {
 | 
					@keyframes backgroundColorPalette-2 {
 | 
				
			||||||
    0% {
 | 
					  0% {
 | 
				
			||||||
        background: var(--bs-chill-green-dark);
 | 
					    background: var(--bs-chill-green-dark);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    25% {
 | 
					  25% {
 | 
				
			||||||
        background: var(--bs-chill-green);
 | 
					    background: var(--bs-chill-green);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    65% {
 | 
					  65% {
 | 
				
			||||||
        background: var(--bs-chill-beige);
 | 
					    background: var(--bs-chill-beige);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    100% {
 | 
					  100% {
 | 
				
			||||||
        background: unset;
 | 
					    background: unset;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,54 +3,54 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
 | 
				
			|||||||
import { reactive } from "vue";
 | 
					import { reactive } from "vue";
 | 
				
			||||||
import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
 | 
					import HistoryButtonList from "ChillDocStoreAssets/vuejs/StoredObjectButton/HistoryButton/HistoryButtonList.vue";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "./../../../types";
 | 
					} from "./../../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonListConfig {
 | 
					interface HistoryButtonListConfig {
 | 
				
			||||||
    versions: StoredObjectVersionWithPointInTime[];
 | 
					  versions: StoredObjectVersionWithPointInTime[];
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    canEdit: boolean;
 | 
					  canEdit: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
					  restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface HistoryButtonModalState {
 | 
					interface HistoryButtonModalState {
 | 
				
			||||||
    opened: boolean;
 | 
					  opened: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<HistoryButtonListConfig>();
 | 
					const props = defineProps<HistoryButtonListConfig>();
 | 
				
			||||||
const state = reactive<HistoryButtonModalState>({ opened: false });
 | 
					const state = reactive<HistoryButtonModalState>({ opened: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const open = () => {
 | 
					const open = () => {
 | 
				
			||||||
    state.opened = true;
 | 
					  state.opened = true;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const onRestoreVersion = (payload: {
 | 
					const onRestoreVersion = (payload: {
 | 
				
			||||||
    newVersion: StoredObjectVersionWithPointInTime;
 | 
					  newVersion: StoredObjectVersionWithPointInTime;
 | 
				
			||||||
}) => emit("restoreVersion", payload);
 | 
					}) => emit("restoreVersion", payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineExpose({ open });
 | 
					defineExpose({ open });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <Teleport to="body">
 | 
					  <Teleport to="body">
 | 
				
			||||||
        <modal v-if="state.opened" @close="state.opened = false">
 | 
					    <modal v-if="state.opened" @close="state.opened = false">
 | 
				
			||||||
            <template v-slot:header>
 | 
					      <template v-slot:header>
 | 
				
			||||||
                <h3>Historique des versions du document</h3>
 | 
					        <h3>Historique des versions du document</h3>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
            <template v-slot:body>
 | 
					      <template v-slot:body>
 | 
				
			||||||
                <p>Les versions sont conservées pendant 90 jours.</p>
 | 
					        <p>Les versions sont conservées pendant 90 jours.</p>
 | 
				
			||||||
                <history-button-list
 | 
					        <history-button-list
 | 
				
			||||||
                    :versions="props.versions"
 | 
					          :versions="props.versions"
 | 
				
			||||||
                    :can-edit="canEdit"
 | 
					          :can-edit="canEdit"
 | 
				
			||||||
                    :stored-object="storedObject"
 | 
					          :stored-object="storedObject"
 | 
				
			||||||
                    @restore-version="onRestoreVersion"
 | 
					          @restore-version="onRestoreVersion"
 | 
				
			||||||
                ></history-button-list>
 | 
					        ></history-button-list>
 | 
				
			||||||
            </template>
 | 
					      </template>
 | 
				
			||||||
        </modal>
 | 
					    </modal>
 | 
				
			||||||
    </Teleport>
 | 
					  </Teleport>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObjectVersionPersisted,
 | 
					  StoredObjectVersionPersisted,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "../../../types";
 | 
					} from "../../../types";
 | 
				
			||||||
import { useToast } from "vue-toast-notification";
 | 
					import { useToast } from "vue-toast-notification";
 | 
				
			||||||
import { restore_version } from "./api";
 | 
					import { restore_version } from "./api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface RestoreVersionButtonProps {
 | 
					interface RestoreVersionButtonProps {
 | 
				
			||||||
    storedObjectVersion: StoredObjectVersionPersisted;
 | 
					  storedObjectVersion: StoredObjectVersionPersisted;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const emit = defineEmits<{
 | 
					const emit = defineEmits<{
 | 
				
			||||||
    restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
					  restoreVersion: [newVersion: StoredObjectVersionWithPointInTime];
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<RestoreVersionButtonProps>();
 | 
					const props = defineProps<RestoreVersionButtonProps>();
 | 
				
			||||||
@@ -19,21 +19,21 @@ const props = defineProps<RestoreVersionButtonProps>();
 | 
				
			|||||||
const $toast = useToast();
 | 
					const $toast = useToast();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const restore_version_fn = async () => {
 | 
					const restore_version_fn = async () => {
 | 
				
			||||||
    const newVersion = await restore_version(props.storedObjectVersion);
 | 
					  const newVersion = await restore_version(props.storedObjectVersion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $toast.success("Version restaurée");
 | 
					  $toast.success("Version restaurée");
 | 
				
			||||||
    emit("restoreVersion", { newVersion });
 | 
					  emit("restoreVersion", { newVersion });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <button
 | 
					  <button
 | 
				
			||||||
        class="btn btn-outline-action"
 | 
					    class="btn btn-outline-action"
 | 
				
			||||||
        @click="restore_version_fn"
 | 
					    @click="restore_version_fn"
 | 
				
			||||||
        title="Restaurer"
 | 
					    title="Restaurer"
 | 
				
			||||||
    >
 | 
					  >
 | 
				
			||||||
        <i class="fa fa-rotate-left"></i> Restaurer
 | 
					    <i class="fa fa-rotate-left"></i> Restaurer
 | 
				
			||||||
    </button>
 | 
					  </button>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss"></style>
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +1,33 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectVersionPersisted,
 | 
					  StoredObjectVersionPersisted,
 | 
				
			||||||
    StoredObjectVersionWithPointInTime,
 | 
					  StoredObjectVersionWithPointInTime,
 | 
				
			||||||
} from "../../../types";
 | 
					} from "../../../types";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    fetchResults,
 | 
					  fetchResults,
 | 
				
			||||||
    makeFetch,
 | 
					  makeFetch,
 | 
				
			||||||
} from "../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
					} from "../../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const get_versions = async (
 | 
					export const get_versions = async (
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
): Promise<StoredObjectVersionWithPointInTime[]> => {
 | 
					): Promise<StoredObjectVersionWithPointInTime[]> => {
 | 
				
			||||||
    const versions = await fetchResults<StoredObjectVersionWithPointInTime>(
 | 
					  const versions = await fetchResults<StoredObjectVersionWithPointInTime>(
 | 
				
			||||||
        `/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`,
 | 
					    `/api/1.0/doc-store/stored-object/${storedObject.uuid}/versions`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return versions.sort(
 | 
					  return versions.sort(
 | 
				
			||||||
        (
 | 
					    (
 | 
				
			||||||
            a: StoredObjectVersionWithPointInTime,
 | 
					      a: StoredObjectVersionWithPointInTime,
 | 
				
			||||||
            b: StoredObjectVersionWithPointInTime,
 | 
					      b: StoredObjectVersionWithPointInTime,
 | 
				
			||||||
        ) => b.version - a.version,
 | 
					    ) => b.version - a.version,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const restore_version = async (
 | 
					export const restore_version = async (
 | 
				
			||||||
    version: StoredObjectVersionPersisted,
 | 
					  version: StoredObjectVersionPersisted,
 | 
				
			||||||
): Promise<StoredObjectVersionWithPointInTime> => {
 | 
					): Promise<StoredObjectVersionWithPointInTime> => {
 | 
				
			||||||
    return await makeFetch<null, StoredObjectVersionWithPointInTime>(
 | 
					  return await makeFetch<null, StoredObjectVersionWithPointInTime>(
 | 
				
			||||||
        "POST",
 | 
					    "POST",
 | 
				
			||||||
        `/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`,
 | 
					    `/api/1.0/doc-store/stored-object/restore-from-version/${version.id}`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,27 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <a
 | 
					  <a
 | 
				
			||||||
        :class="Object.assign(props.classes, { btn: true })"
 | 
					    :class="Object.assign(props.classes, { btn: true })"
 | 
				
			||||||
        @click="beforeLeave($event)"
 | 
					    @click="beforeLeave($event)"
 | 
				
			||||||
        :href="
 | 
					    :href="build_wopi_editor_link(props.storedObject.uuid, props.returnPath)"
 | 
				
			||||||
            build_wopi_editor_link(props.storedObject.uuid, props.returnPath)
 | 
					  >
 | 
				
			||||||
        "
 | 
					    <i class="fa fa-paragraph"></i>
 | 
				
			||||||
    >
 | 
					    Editer en ligne
 | 
				
			||||||
        <i class="fa fa-paragraph"></i>
 | 
					  </a>
 | 
				
			||||||
        Editer en ligne
 | 
					 | 
				
			||||||
    </a>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import WopiEditButton from "./WopiEditButton.vue";
 | 
					import WopiEditButton from "./WopiEditButton.vue";
 | 
				
			||||||
import { build_wopi_editor_link } from "./helpers";
 | 
					import { build_wopi_editor_link } from "./helpers";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    WopiEditButtonExecutableBeforeLeaveFunction,
 | 
					  WopiEditButtonExecutableBeforeLeaveFunction,
 | 
				
			||||||
} from "../../types";
 | 
					} from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface WopiEditButtonConfig {
 | 
					interface WopiEditButtonConfig {
 | 
				
			||||||
    storedObject: StoredObject;
 | 
					  storedObject: StoredObject;
 | 
				
			||||||
    returnPath?: string;
 | 
					  returnPath?: string;
 | 
				
			||||||
    classes: Record<string, boolean>;
 | 
					  classes: Record<string, boolean>;
 | 
				
			||||||
    executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
 | 
					  executeBeforeLeave?: WopiEditButtonExecutableBeforeLeaveFunction;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<WopiEditButtonConfig>();
 | 
					const props = defineProps<WopiEditButtonConfig>();
 | 
				
			||||||
@@ -31,24 +29,24 @@ const props = defineProps<WopiEditButtonConfig>();
 | 
				
			|||||||
let executed = false;
 | 
					let executed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function beforeLeave(event: Event): Promise<true> {
 | 
					async function beforeLeave(event: Event): Promise<true> {
 | 
				
			||||||
    if (props.executeBeforeLeave === undefined || executed === true) {
 | 
					  if (props.executeBeforeLeave === undefined || executed === true) {
 | 
				
			||||||
        return Promise.resolve(true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    event.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await props.executeBeforeLeave();
 | 
					 | 
				
			||||||
    executed = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const link = event.target as HTMLAnchorElement;
 | 
					 | 
				
			||||||
    link.click();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return Promise.resolve(true);
 | 
					    return Promise.resolve(true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await props.executeBeforeLeave();
 | 
				
			||||||
 | 
					  executed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const link = event.target as HTMLAnchorElement;
 | 
				
			||||||
 | 
					  link.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Promise.resolve(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss">
 | 
				
			||||||
i.fa::before {
 | 
					i.fa::before {
 | 
				
			||||||
    color: var(--bs-dropdown-link-hover-color);
 | 
					  color: var(--bs-dropdown-link-hover-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,235 +1,230 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    StoredObject,
 | 
					  StoredObject,
 | 
				
			||||||
    StoredObjectStatus,
 | 
					  StoredObjectStatus,
 | 
				
			||||||
    StoredObjectStatusChange,
 | 
					  StoredObjectStatusChange,
 | 
				
			||||||
    StoredObjectVersion,
 | 
					  StoredObjectVersion,
 | 
				
			||||||
} from "../../types";
 | 
					} from "../../types";
 | 
				
			||||||
import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
					import { makeFetch } from "../../../../../ChillMainBundle/Resources/public/lib/api/apiMethods";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MIMES_EDIT = new Set([
 | 
					const MIMES_EDIT = new Set([
 | 
				
			||||||
    "application/vnd.ms-powerpoint",
 | 
					  "application/vnd.ms-powerpoint",
 | 
				
			||||||
    "application/vnd.ms-excel",
 | 
					  "application/vnd.ms-excel",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.text",
 | 
					  "application/vnd.oasis.opendocument.text",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.text-flat-xml",
 | 
					  "application/vnd.oasis.opendocument.text-flat-xml",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.spreadsheet",
 | 
					  "application/vnd.oasis.opendocument.spreadsheet",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.spreadsheet-flat-xml",
 | 
					  "application/vnd.oasis.opendocument.spreadsheet-flat-xml",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.presentation",
 | 
					  "application/vnd.oasis.opendocument.presentation",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.presentation-flat-xml",
 | 
					  "application/vnd.oasis.opendocument.presentation-flat-xml",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.graphics",
 | 
					  "application/vnd.oasis.opendocument.graphics",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.graphics-flat-xml",
 | 
					  "application/vnd.oasis.opendocument.graphics-flat-xml",
 | 
				
			||||||
    "application/vnd.oasis.opendocument.chart",
 | 
					  "application/vnd.oasis.opendocument.chart",
 | 
				
			||||||
    "application/msword",
 | 
					  "application/msword",
 | 
				
			||||||
    "application/vnd.ms-excel",
 | 
					  "application/vnd.ms-excel",
 | 
				
			||||||
    "application/vnd.ms-powerpoint",
 | 
					  "application/vnd.ms-powerpoint",
 | 
				
			||||||
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
 | 
					  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
 | 
				
			||||||
    "application/vnd.ms-word.document.macroEnabled.12",
 | 
					  "application/vnd.ms-word.document.macroEnabled.12",
 | 
				
			||||||
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
 | 
					  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
 | 
				
			||||||
    "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
 | 
					  "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
 | 
				
			||||||
    "application/vnd.ms-excel.sheet.macroEnabled.12",
 | 
					  "application/vnd.ms-excel.sheet.macroEnabled.12",
 | 
				
			||||||
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
 | 
					  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
 | 
				
			||||||
    "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
 | 
					  "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
 | 
				
			||||||
    "application/x-dif-document",
 | 
					  "application/x-dif-document",
 | 
				
			||||||
    "text/spreadsheet",
 | 
					  "text/spreadsheet",
 | 
				
			||||||
    "text/csv",
 | 
					  "text/csv",
 | 
				
			||||||
    "application/x-dbase",
 | 
					  "application/x-dbase",
 | 
				
			||||||
    "text/rtf",
 | 
					  "text/rtf",
 | 
				
			||||||
    "text/plain",
 | 
					  "text/plain",
 | 
				
			||||||
    "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
 | 
					  "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MIMES_VIEW = new Set([
 | 
					const MIMES_VIEW = new Set([
 | 
				
			||||||
    ...MIMES_EDIT,
 | 
					  ...MIMES_EDIT,
 | 
				
			||||||
    [
 | 
					  [
 | 
				
			||||||
        "image/svg+xml",
 | 
					    "image/svg+xml",
 | 
				
			||||||
        "application/vnd.sun.xml.writer",
 | 
					    "application/vnd.sun.xml.writer",
 | 
				
			||||||
        "application/vnd.sun.xml.calc",
 | 
					    "application/vnd.sun.xml.calc",
 | 
				
			||||||
        "application/vnd.sun.xml.impress",
 | 
					    "application/vnd.sun.xml.impress",
 | 
				
			||||||
        "application/vnd.sun.xml.draw",
 | 
					    "application/vnd.sun.xml.draw",
 | 
				
			||||||
        "application/vnd.sun.xml.writer.global",
 | 
					    "application/vnd.sun.xml.writer.global",
 | 
				
			||||||
        "application/vnd.sun.xml.writer.template",
 | 
					    "application/vnd.sun.xml.writer.template",
 | 
				
			||||||
        "application/vnd.sun.xml.calc.template",
 | 
					    "application/vnd.sun.xml.calc.template",
 | 
				
			||||||
        "application/vnd.sun.xml.impress.template",
 | 
					    "application/vnd.sun.xml.impress.template",
 | 
				
			||||||
        "application/vnd.sun.xml.draw.template",
 | 
					    "application/vnd.sun.xml.draw.template",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.text-master",
 | 
					    "application/vnd.oasis.opendocument.text-master",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.text-template",
 | 
					    "application/vnd.oasis.opendocument.text-template",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.text-master-template",
 | 
					    "application/vnd.oasis.opendocument.text-master-template",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.spreadsheet-template",
 | 
					    "application/vnd.oasis.opendocument.spreadsheet-template",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.presentation-template",
 | 
					    "application/vnd.oasis.opendocument.presentation-template",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.graphics-template",
 | 
					    "application/vnd.oasis.opendocument.graphics-template",
 | 
				
			||||||
        "application/vnd.ms-word.template.macroEnabled.12",
 | 
					    "application/vnd.ms-word.template.macroEnabled.12",
 | 
				
			||||||
        "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
 | 
					    "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
 | 
				
			||||||
        "application/vnd.ms-excel.template.macroEnabled.12",
 | 
					    "application/vnd.ms-excel.template.macroEnabled.12",
 | 
				
			||||||
        "application/vnd.openxmlformats-officedocument.presentationml.template",
 | 
					    "application/vnd.openxmlformats-officedocument.presentationml.template",
 | 
				
			||||||
        "application/vnd.ms-powerpoint.template.macroEnabled.12",
 | 
					    "application/vnd.ms-powerpoint.template.macroEnabled.12",
 | 
				
			||||||
        "application/vnd.wordperfect",
 | 
					    "application/vnd.wordperfect",
 | 
				
			||||||
        "application/x-aportisdoc",
 | 
					    "application/x-aportisdoc",
 | 
				
			||||||
        "application/x-hwp",
 | 
					    "application/x-hwp",
 | 
				
			||||||
        "application/vnd.ms-works",
 | 
					    "application/vnd.ms-works",
 | 
				
			||||||
        "application/x-mswrite",
 | 
					    "application/x-mswrite",
 | 
				
			||||||
        "application/vnd.lotus-1-2-3",
 | 
					    "application/vnd.lotus-1-2-3",
 | 
				
			||||||
        "image/cgm",
 | 
					    "image/cgm",
 | 
				
			||||||
        "image/vnd.dxf",
 | 
					    "image/vnd.dxf",
 | 
				
			||||||
        "image/x-emf",
 | 
					    "image/x-emf",
 | 
				
			||||||
        "image/x-wmf",
 | 
					    "image/x-wmf",
 | 
				
			||||||
        "application/coreldraw",
 | 
					    "application/coreldraw",
 | 
				
			||||||
        "application/vnd.visio2013",
 | 
					    "application/vnd.visio2013",
 | 
				
			||||||
        "application/vnd.visio",
 | 
					    "application/vnd.visio",
 | 
				
			||||||
        "application/vnd.ms-visio.drawing",
 | 
					    "application/vnd.ms-visio.drawing",
 | 
				
			||||||
        "application/x-mspublisher",
 | 
					    "application/x-mspublisher",
 | 
				
			||||||
        "application/x-sony-bbeb",
 | 
					    "application/x-sony-bbeb",
 | 
				
			||||||
        "application/x-gnumeric",
 | 
					    "application/x-gnumeric",
 | 
				
			||||||
        "application/macwriteii",
 | 
					    "application/macwriteii",
 | 
				
			||||||
        "application/x-iwork-numbers-sffnumbers",
 | 
					    "application/x-iwork-numbers-sffnumbers",
 | 
				
			||||||
        "application/vnd.oasis.opendocument.text-web",
 | 
					    "application/vnd.oasis.opendocument.text-web",
 | 
				
			||||||
        "application/x-pagemaker",
 | 
					    "application/x-pagemaker",
 | 
				
			||||||
        "application/x-fictionbook+xml",
 | 
					    "application/x-fictionbook+xml",
 | 
				
			||||||
        "application/clarisworks",
 | 
					    "application/clarisworks",
 | 
				
			||||||
        "image/x-wpg",
 | 
					    "image/x-wpg",
 | 
				
			||||||
        "application/x-iwork-pages-sffpages",
 | 
					    "application/x-iwork-pages-sffpages",
 | 
				
			||||||
        "application/x-iwork-keynote-sffkey",
 | 
					    "application/x-iwork-keynote-sffkey",
 | 
				
			||||||
        "application/x-abiword",
 | 
					    "application/x-abiword",
 | 
				
			||||||
        "image/x-freehand",
 | 
					    "image/x-freehand",
 | 
				
			||||||
        "application/vnd.sun.xml.chart",
 | 
					    "application/vnd.sun.xml.chart",
 | 
				
			||||||
        "application/x-t602",
 | 
					    "application/x-t602",
 | 
				
			||||||
        "image/bmp",
 | 
					    "image/bmp",
 | 
				
			||||||
        "image/png",
 | 
					    "image/png",
 | 
				
			||||||
        "image/gif",
 | 
					    "image/gif",
 | 
				
			||||||
        "image/tiff",
 | 
					    "image/tiff",
 | 
				
			||||||
        "image/jpg",
 | 
					    "image/jpg",
 | 
				
			||||||
        "image/jpeg",
 | 
					    "image/jpeg",
 | 
				
			||||||
        "application/pdf",
 | 
					    "application/pdf",
 | 
				
			||||||
    ],
 | 
					  ],
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SignedUrlGet {
 | 
					export interface SignedUrlGet {
 | 
				
			||||||
    method: "GET" | "HEAD";
 | 
					  method: "GET" | "HEAD";
 | 
				
			||||||
    url: string;
 | 
					  url: string;
 | 
				
			||||||
    expires: number;
 | 
					  expires: number;
 | 
				
			||||||
    object_name: string;
 | 
					  object_name: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function is_extension_editable(mimeType: string): boolean {
 | 
					function is_extension_editable(mimeType: string): boolean {
 | 
				
			||||||
    return MIMES_EDIT.has(mimeType);
 | 
					  return MIMES_EDIT.has(mimeType);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function is_extension_viewable(mimeType: string): boolean {
 | 
					function is_extension_viewable(mimeType: string): boolean {
 | 
				
			||||||
    return MIMES_VIEW.has(mimeType);
 | 
					  return MIMES_VIEW.has(mimeType);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function build_convert_link(uuid: string) {
 | 
					function build_convert_link(uuid: string) {
 | 
				
			||||||
    return `/chill/wopi/convert/${uuid}`;
 | 
					  return `/chill/wopi/convert/${uuid}`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function build_download_info_link(
 | 
					function build_download_info_link(
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
    atVersion: null | StoredObjectVersion,
 | 
					  atVersion: null | StoredObjectVersion,
 | 
				
			||||||
): string {
 | 
					): string {
 | 
				
			||||||
    const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`;
 | 
					  const url = `/api/1.0/doc-store/async-upload/temp_url/${storedObject.uuid}/generate/get`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null !== atVersion) {
 | 
					  if (null !== atVersion) {
 | 
				
			||||||
        const params = new URLSearchParams({ version: atVersion.filename });
 | 
					    const params = new URLSearchParams({ version: atVersion.filename });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return url + "?" + params.toString();
 | 
					    return url + "?" + params.toString();
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return url;
 | 
					  return url;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function download_info_link(
 | 
					async function download_info_link(
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
    atVersion: null | StoredObjectVersion,
 | 
					  atVersion: null | StoredObjectVersion,
 | 
				
			||||||
): Promise<SignedUrlGet> {
 | 
					): Promise<SignedUrlGet> {
 | 
				
			||||||
    return makeFetch("GET", build_download_info_link(storedObject, atVersion));
 | 
					  return makeFetch("GET", build_download_info_link(storedObject, atVersion));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function build_wopi_editor_link(uuid: string, returnPath?: string) {
 | 
					function build_wopi_editor_link(uuid: string, returnPath?: string) {
 | 
				
			||||||
    if (returnPath === undefined) {
 | 
					  if (returnPath === undefined) {
 | 
				
			||||||
        returnPath =
 | 
					    returnPath =
 | 
				
			||||||
            window.location.pathname +
 | 
					      window.location.pathname + window.location.search + window.location.hash;
 | 
				
			||||||
            window.location.search +
 | 
					  }
 | 
				
			||||||
            window.location.hash;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					  return (
 | 
				
			||||||
        `/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath)
 | 
					    `/chill/wopi/edit/${uuid}?returnPath=` + encodeURIComponent(returnPath)
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function download_doc(url: string): Promise<Blob> {
 | 
					function download_doc(url: string): Promise<Blob> {
 | 
				
			||||||
    return window.fetch(url).then((r) => {
 | 
					  return window.fetch(url).then((r) => {
 | 
				
			||||||
        if (r.ok) {
 | 
					    if (r.ok) {
 | 
				
			||||||
            return r.blob();
 | 
					      return r.blob();
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        throw new Error("Could not download document");
 | 
					    throw new Error("Could not download document");
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function download_and_decrypt_doc(
 | 
					async function download_and_decrypt_doc(
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
    atVersion: null | StoredObjectVersion,
 | 
					  atVersion: null | StoredObjectVersion,
 | 
				
			||||||
): Promise<Blob> {
 | 
					): Promise<Blob> {
 | 
				
			||||||
    const algo = "AES-CBC";
 | 
					  const algo = "AES-CBC";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const atVersionToDownload = atVersion ?? storedObject.currentVersion;
 | 
					  const atVersionToDownload = atVersion ?? storedObject.currentVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null === atVersionToDownload) {
 | 
					  if (null === atVersionToDownload) {
 | 
				
			||||||
        throw new Error("no version associated to stored object");
 | 
					    throw new Error("no version associated to stored object");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // sometimes, the downloadInfo may be embedded into the storedObject
 | 
					  // sometimes, the downloadInfo may be embedded into the storedObject
 | 
				
			||||||
    console.log("storedObject", storedObject);
 | 
					  console.log("storedObject", storedObject);
 | 
				
			||||||
    let downloadInfo;
 | 
					  let downloadInfo;
 | 
				
			||||||
    if (
 | 
					  if (
 | 
				
			||||||
        typeof storedObject._links !== "undefined" &&
 | 
					    typeof storedObject._links !== "undefined" &&
 | 
				
			||||||
        typeof storedObject._links.downloadLink !== "undefined"
 | 
					    typeof storedObject._links.downloadLink !== "undefined"
 | 
				
			||||||
    ) {
 | 
					  ) {
 | 
				
			||||||
        downloadInfo = storedObject._links.downloadLink;
 | 
					    downloadInfo = storedObject._links.downloadLink;
 | 
				
			||||||
    } else {
 | 
					  } else {
 | 
				
			||||||
        downloadInfo = await download_info_link(
 | 
					    downloadInfo = await download_info_link(storedObject, atVersionToDownload);
 | 
				
			||||||
            storedObject,
 | 
					  }
 | 
				
			||||||
            atVersionToDownload,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const rawResponse = await window.fetch(downloadInfo.url);
 | 
					  const rawResponse = await window.fetch(downloadInfo.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!rawResponse.ok) {
 | 
					  if (!rawResponse.ok) {
 | 
				
			||||||
        throw new Error(
 | 
					    throw new Error(
 | 
				
			||||||
            "error while downloading raw file " +
 | 
					      "error while downloading raw file " +
 | 
				
			||||||
                rawResponse.status +
 | 
					        rawResponse.status +
 | 
				
			||||||
                " " +
 | 
					        " " +
 | 
				
			||||||
                rawResponse.statusText,
 | 
					        rawResponse.statusText,
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (atVersionToDownload.iv.length === 0) {
 | 
					  if (atVersionToDownload.iv.length === 0) {
 | 
				
			||||||
        return rawResponse.blob();
 | 
					    return rawResponse.blob();
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const rawBuffer = await rawResponse.arrayBuffer();
 | 
					  const rawBuffer = await rawResponse.arrayBuffer();
 | 
				
			||||||
    try {
 | 
					  try {
 | 
				
			||||||
        const key = await window.crypto.subtle.importKey(
 | 
					    const key = await window.crypto.subtle.importKey(
 | 
				
			||||||
            "jwk",
 | 
					      "jwk",
 | 
				
			||||||
            atVersionToDownload.keyInfos,
 | 
					      atVersionToDownload.keyInfos,
 | 
				
			||||||
            { name: algo },
 | 
					      { name: algo },
 | 
				
			||||||
            false,
 | 
					      false,
 | 
				
			||||||
            ["decrypt"],
 | 
					      ["decrypt"],
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
        const iv = Uint8Array.from(atVersionToDownload.iv);
 | 
					    const iv = Uint8Array.from(atVersionToDownload.iv);
 | 
				
			||||||
        const decrypted = await window.crypto.subtle.decrypt(
 | 
					    const decrypted = await window.crypto.subtle.decrypt(
 | 
				
			||||||
            { name: algo, iv: iv },
 | 
					      { name: algo, iv: iv },
 | 
				
			||||||
            key,
 | 
					      key,
 | 
				
			||||||
            rawBuffer,
 | 
					      rawBuffer,
 | 
				
			||||||
        );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Promise.resolve(new Blob([decrypted]));
 | 
					    return Promise.resolve(new Blob([decrypted]));
 | 
				
			||||||
    } catch (e) {
 | 
					  } catch (e) {
 | 
				
			||||||
        console.error("encounter error while keys and decrypt operations");
 | 
					    console.error("encounter error while keys and decrypt operations");
 | 
				
			||||||
        console.error(e);
 | 
					    console.error(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        throw e;
 | 
					    throw e;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -239,48 +234,45 @@ async function download_and_decrypt_doc(
 | 
				
			|||||||
 * storage.
 | 
					 * storage.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function download_doc_as_pdf(storedObject: StoredObject): Promise<Blob> {
 | 
					async function download_doc_as_pdf(storedObject: StoredObject): Promise<Blob> {
 | 
				
			||||||
    if (null === storedObject.currentVersion) {
 | 
					  if (null === storedObject.currentVersion) {
 | 
				
			||||||
        throw new Error("the stored object does not count any version");
 | 
					    throw new Error("the stored object does not count any version");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (storedObject.currentVersion?.type === "application/pdf") {
 | 
					  if (storedObject.currentVersion?.type === "application/pdf") {
 | 
				
			||||||
        return download_and_decrypt_doc(
 | 
					    return download_and_decrypt_doc(storedObject, storedObject.currentVersion);
 | 
				
			||||||
            storedObject,
 | 
					  }
 | 
				
			||||||
            storedObject.currentVersion,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const convertLink = build_convert_link(storedObject.uuid);
 | 
					  const convertLink = build_convert_link(storedObject.uuid);
 | 
				
			||||||
    const response = await fetch(convertLink);
 | 
					  const response = await fetch(convertLink);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!response.ok) {
 | 
					  if (!response.ok) {
 | 
				
			||||||
        throw new Error("Could not convert the document: " + response.status);
 | 
					    throw new Error("Could not convert the document: " + response.status);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return response.blob();
 | 
					  return response.blob();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function is_object_ready(
 | 
					async function is_object_ready(
 | 
				
			||||||
    storedObject: StoredObject,
 | 
					  storedObject: StoredObject,
 | 
				
			||||||
): Promise<StoredObjectStatusChange> {
 | 
					): Promise<StoredObjectStatusChange> {
 | 
				
			||||||
    const new_status_response = await window.fetch(
 | 
					  const new_status_response = await window.fetch(
 | 
				
			||||||
        `/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`,
 | 
					    `/api/1.0/doc-store/stored-object/${storedObject.uuid}/is-ready`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!new_status_response.ok) {
 | 
					  if (!new_status_response.ok) {
 | 
				
			||||||
        throw new Error("could not fetch the new status");
 | 
					    throw new Error("could not fetch the new status");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return await new_status_response.json();
 | 
					  return await new_status_response.json();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
    build_convert_link,
 | 
					  build_convert_link,
 | 
				
			||||||
    build_wopi_editor_link,
 | 
					  build_wopi_editor_link,
 | 
				
			||||||
    download_and_decrypt_doc,
 | 
					  download_and_decrypt_doc,
 | 
				
			||||||
    download_doc,
 | 
					  download_doc,
 | 
				
			||||||
    download_doc_as_pdf,
 | 
					  download_doc_as_pdf,
 | 
				
			||||||
    is_extension_editable,
 | 
					  is_extension_editable,
 | 
				
			||||||
    is_extension_viewable,
 | 
					  is_extension_viewable,
 | 
				
			||||||
    is_object_ready,
 | 
					  is_object_ready,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,11 +43,17 @@ class StoredObjectVersionNormalizer implements NormalizerInterface, NormalizerAw
 | 
				
			|||||||
            'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, [...$context, UserNormalizer::AT_DATE => $object->getCreatedAt()]),
 | 
					            'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, [...$context, UserNormalizer::AT_DATE => $object->getCreatedAt()]),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (in_array(self::WITH_POINT_IN_TIMES_CONTEXT, $context[AbstractNormalizer::GROUPS] ?? [], true)) {
 | 
					        $normalizationGroups = $context[AbstractNormalizer::GROUPS] ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_string($normalizationGroups)) {
 | 
				
			||||||
 | 
					            $normalizationGroups = [$normalizationGroups];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (in_array(self::WITH_POINT_IN_TIMES_CONTEXT, $normalizationGroups, true)) {
 | 
				
			||||||
            $data['point-in-times'] = $this->normalizer->normalize($object->getPointInTimes(), $format, $context);
 | 
					            $data['point-in-times'] = $this->normalizer->normalize($object->getPointInTimes(), $format, $context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (in_array(self::WITH_RESTORED_CONTEXT, $context[AbstractNormalizer::GROUPS] ?? [], true)) {
 | 
					        if (in_array(self::WITH_RESTORED_CONTEXT, $normalizationGroups, true)) {
 | 
				
			||||||
            $data['from-restored'] = $this->normalizer->normalize($object->getCreatedFrom(), $format, [AbstractNormalizer::GROUPS => ['read']]);
 | 
					            $data['from-restored'] = $this->normalizer->normalize($object->getCreatedFrom(), $format, [AbstractNormalizer::GROUPS => ['read']]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,14 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <location />
 | 
					  <location />
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import Location from "ChillActivityAssets/vuejs/Activity/components/Location.vue";
 | 
					import Location from "ChillActivityAssets/vuejs/Activity/components/Location.vue";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    name: "App",
 | 
					  name: "App",
 | 
				
			||||||
    components: {
 | 
					  components: {
 | 
				
			||||||
        Location,
 | 
					    Location,
 | 
				
			||||||
    },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,6 @@ use Chill\MainBundle\Entity\User;
 | 
				
			|||||||
use Chill\MainBundle\Entity\Address;
 | 
					use Chill\MainBundle\Entity\Address;
 | 
				
			||||||
use libphonenumber\PhoneNumber;
 | 
					use libphonenumber\PhoneNumber;
 | 
				
			||||||
use Symfony\Component\Validator\Constraints as Assert;
 | 
					use Symfony\Component\Validator\Constraints as Assert;
 | 
				
			||||||
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Immersion.
 | 
					 * Immersion.
 | 
				
			||||||
@@ -86,14 +85,14 @@ class Immersion implements \Stringable
 | 
				
			|||||||
     * @Assert\NotBlank()
 | 
					     * @Assert\NotBlank()
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    #[ORM\Column(name: 'tuteurPhoneNumber', type: 'phone_number', nullable: true)]
 | 
					    #[ORM\Column(name: 'tuteurPhoneNumber', type: 'phone_number', nullable: true)]
 | 
				
			||||||
    #[PhonenumberConstraint(type: 'any')]
 | 
					    #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber]
 | 
				
			||||||
    private ?PhoneNumber $tuteurPhoneNumber = null;
 | 
					    private ?PhoneNumber $tuteurPhoneNumber = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[ORM\Column(name: 'structureAccName', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
 | 
					    #[ORM\Column(name: 'structureAccName', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
 | 
				
			||||||
    private ?string $structureAccName = null;
 | 
					    private ?string $structureAccName = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[ORM\Column(name: 'structureAccPhonenumber', type: 'phone_number', nullable: true)]
 | 
					    #[ORM\Column(name: 'structureAccPhonenumber', type: 'phone_number', nullable: true)]
 | 
				
			||||||
    #[PhonenumberConstraint(type: 'any')]
 | 
					    #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber]
 | 
				
			||||||
    private ?PhoneNumber $structureAccPhonenumber = null;
 | 
					    private ?PhoneNumber $structureAccPhonenumber = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[ORM\Column(name: 'structureAccEmail', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
 | 
					    #[ORM\Column(name: 'structureAccEmail', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ namespace Chill\MainBundle;
 | 
				
			|||||||
use Chill\MainBundle\Cron\CronJobInterface;
 | 
					use Chill\MainBundle\Cron\CronJobInterface;
 | 
				
			||||||
use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass;
 | 
					use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass;
 | 
				
			||||||
use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass;
 | 
					use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass;
 | 
				
			||||||
use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass;
 | 
					 | 
				
			||||||
use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompilerPass;
 | 
					use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompilerPass;
 | 
				
			||||||
use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass;
 | 
					use Chill\MainBundle\DependencyInjection\CompilerPass\SearchableServicesCompilerPass;
 | 
				
			||||||
use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass;
 | 
					use Chill\MainBundle\DependencyInjection\CompilerPass\TimelineCompilerClass;
 | 
				
			||||||
@@ -70,7 +69,6 @@ class ChillMainBundle extends Bundle
 | 
				
			|||||||
        $container->addCompilerPass(new TimelineCompilerClass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					        $container->addCompilerPass(new TimelineCompilerClass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
				
			||||||
        $container->addCompilerPass(new WidgetsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					        $container->addCompilerPass(new WidgetsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
				
			||||||
        $container->addCompilerPass(new NotificationCounterCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					        $container->addCompilerPass(new NotificationCounterCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
				
			||||||
        $container->addCompilerPass(new MenuCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					 | 
				
			||||||
        $container->addCompilerPass(new ACLFlagsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					        $container->addCompilerPass(new ACLFlagsCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
				
			||||||
        $container->addCompilerPass(new CRUDControllerCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
					        $container->addCompilerPass(new CRUDControllerCompilerPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare(strict_types=1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Chill is a software for social workers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * For the full copyright and license information, please view
 | 
				
			||||||
 | 
					 * the LICENSE file that was distributed with this source code.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Chill\MainBundle\Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Command\Command;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputArgument;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Input\InputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Console\Output\OutputInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\HttpKernel\KernelInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Translation\MessageCatalogue;
 | 
				
			||||||
 | 
					use Symfony\Component\Translation\Reader\TranslationReaderInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Translation\Writer\TranslationWriterInterface;
 | 
				
			||||||
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OverrideTranslationCommand extends Command
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function __construct(
 | 
				
			||||||
 | 
					        private readonly TranslationReaderInterface $reader,
 | 
				
			||||||
 | 
					        private readonly TranslationWriterInterface $writer,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        $this->setName('chill:main:override_translation');
 | 
				
			||||||
 | 
					        parent::__construct();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function configure(): void
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this
 | 
				
			||||||
 | 
					            ->setDescription('Generate a translation catalogue with translation remplacements based on replacements provided in a YAML file.')
 | 
				
			||||||
 | 
					            ->addArgument('locale', InputArgument::REQUIRED, 'The locale to process (e.g. fr, en).')
 | 
				
			||||||
 | 
					            ->addArgument('overrides', InputArgument::REQUIRED, 'Path to the overrides YAML file (list of {from, to}).');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function execute(InputInterface $input, OutputInterface $output): int
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $locale = (string) $input->getArgument('locale');
 | 
				
			||||||
 | 
					        $overridesPath = (string) $input->getArgument('overrides');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $catalogue = $this->loadCatalogue($locale);
 | 
				
			||||||
 | 
					        $overrides = $this->loadOverrides($overridesPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $toOverrideCatalogue = new MessageCatalogue($locale);
 | 
				
			||||||
 | 
					        foreach ($catalogue->getDomains() as $domain) {
 | 
				
			||||||
 | 
					            // hack: we have to replace the suffix ".intl-icu" by "+intl-ic"
 | 
				
			||||||
 | 
					            $domain = str_replace('.intl-icu', '+intl-icu', $domain);
 | 
				
			||||||
 | 
					            foreach ($catalogue->all($domain) as $key => $translation) {
 | 
				
			||||||
 | 
					                foreach ($overrides as $changes) {
 | 
				
			||||||
 | 
					                    $from = $changes['from'];
 | 
				
			||||||
 | 
					                    $to = $changes['to'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (is_string($translation) && str_contains($translation, $from)) {
 | 
				
			||||||
 | 
					                        $newTranslation = strtr($translation, [$from => $to]);
 | 
				
			||||||
 | 
					                        $toOverrideCatalogue->set($key, $newTranslation, $domain);
 | 
				
			||||||
 | 
					                        $translation = $newTranslation;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /** @var KernelInterface $kernel */
 | 
				
			||||||
 | 
					        /* @phpstan-ignore-next-line */
 | 
				
			||||||
 | 
					        $kernel = $this->getApplication()->getKernel();
 | 
				
			||||||
 | 
					        $outputDir = rtrim($kernel->getProjectDir(), '/').'/translations';
 | 
				
			||||||
 | 
					        if (!is_dir($outputDir)) {
 | 
				
			||||||
 | 
					            @mkdir($outputDir, 0775, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Writer expects the 'path' option to be a directory; it will create the proper file name
 | 
				
			||||||
 | 
					        $this->writer->write($toOverrideCatalogue, 'yaml', ['path' => $outputDir]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $output->writeln(sprintf('Override catalogue written to %s (domain: messages, locale: %s).', $outputDir, $locale));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Command::SUCCESS;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @return list<array{from: string, to: string}>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private function loadOverrides(string $path): array
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return Yaml::parseFile($path);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function loadCatalogue(string $locale): MessageCatalogue
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /** @var KernelInterface $kernel */
 | 
				
			||||||
 | 
					        /* @phpstan-ignore-next-line */
 | 
				
			||||||
 | 
					        $kernel = $this->getApplication()->getKernel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // collect path for translations
 | 
				
			||||||
 | 
					        $transPaths = [];
 | 
				
			||||||
 | 
					        foreach ($kernel->getBundles() as $bundle) {
 | 
				
			||||||
 | 
					            $bundleDir = $bundle->getPath();
 | 
				
			||||||
 | 
					            $transPaths[] = is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundle->getPath().'/translations';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $currentCatalogue = new MessageCatalogue($locale);
 | 
				
			||||||
 | 
					        foreach ($transPaths as $path) {
 | 
				
			||||||
 | 
					            if (is_dir($path)) {
 | 
				
			||||||
 | 
					                $this->reader->read($path, $currentCatalogue);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $currentCatalogue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,9 +14,9 @@ namespace Chill\MainBundle\Entity;
 | 
				
			|||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
 | 
					use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
 | 
				
			||||||
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
 | 
					use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
 | 
				
			||||||
use Chill\MainBundle\Repository\LocationRepository;
 | 
					use Chill\MainBundle\Repository\LocationRepository;
 | 
				
			||||||
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
 | 
					 | 
				
			||||||
use Doctrine\ORM\Mapping as ORM;
 | 
					use Doctrine\ORM\Mapping as ORM;
 | 
				
			||||||
use libphonenumber\PhoneNumber;
 | 
					use libphonenumber\PhoneNumber;
 | 
				
			||||||
 | 
					use Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber as MisdPhoneNumberConstraint;
 | 
				
			||||||
use Symfony\Component\Serializer\Annotation as Serializer;
 | 
					use Symfony\Component\Serializer\Annotation as Serializer;
 | 
				
			||||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
 | 
					use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,12 +67,12 @@ class Location implements TrackCreationInterface, TrackUpdateInterface
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[Serializer\Groups(['read', 'write', 'docgen:read'])]
 | 
					    #[Serializer\Groups(['read', 'write', 'docgen:read'])]
 | 
				
			||||||
    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
					    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
				
			||||||
    #[PhonenumberConstraint(type: 'any')]
 | 
					    #[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::ANY])]
 | 
				
			||||||
    private ?PhoneNumber $phonenumber1 = null;
 | 
					    private ?PhoneNumber $phonenumber1 = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Serializer\Groups(['read', 'write', 'docgen:read'])]
 | 
					    #[Serializer\Groups(['read', 'write', 'docgen:read'])]
 | 
				
			||||||
    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
					    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
				
			||||||
    #[PhonenumberConstraint(type: 'any')]
 | 
					    #[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::ANY])]
 | 
				
			||||||
    private ?PhoneNumber $phonenumber2 = null;
 | 
					    private ?PhoneNumber $phonenumber2 = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Serializer\Groups(['read'])]
 | 
					    #[Serializer\Groups(['read'])]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
 | 
				
			|||||||
use Symfony\Component\Security\Core\User\UserInterface;
 | 
					use Symfony\Component\Security\Core\User\UserInterface;
 | 
				
			||||||
use Symfony\Component\Serializer\Annotation as Serializer;
 | 
					use Symfony\Component\Serializer\Annotation as Serializer;
 | 
				
			||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
 | 
					use Symfony\Component\Validator\Context\ExecutionContextInterface;
 | 
				
			||||||
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * User.
 | 
					 * User.
 | 
				
			||||||
@@ -116,7 +115,7 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
 | 
				
			|||||||
     * The user's mobile phone number.
 | 
					     * The user's mobile phone number.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
					    #[ORM\Column(type: 'phone_number', nullable: true)]
 | 
				
			||||||
    #[PhonenumberConstraint]
 | 
					    #[\Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber]
 | 
				
			||||||
    private ?PhoneNumber $phonenumber = null;
 | 
					    private ?PhoneNumber $phonenumber = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,8 @@ use Symfony\Component\HttpFoundation\Response;
 | 
				
			|||||||
use Symfony\Contracts\Translation\TranslatableInterface;
 | 
					use Symfony\Contracts\Translation\TranslatableInterface;
 | 
				
			||||||
use Symfony\Contracts\Translation\TranslatorInterface;
 | 
					use Symfony\Contracts\Translation\TranslatorInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Create a CSV List for the export.
 | 
					 * Create a CSV List for the export.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,8 @@ interface PhoneNumberHelperInterface
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Return true if the validation is configured and available.
 | 
					     * Return true if the validation is configured and available.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @deprecated this is an internal behaviour of the helper and should not be taken into account outside of the implementation
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function isPhonenumberValidationConfigured(): bool;
 | 
					    public function isPhonenumberValidationConfigured(): bool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,6 +76,24 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
 | 
				
			|||||||
            ->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
 | 
					            ->formatOutOfCountryCallingNumber($phoneNumber, $this->config['default_carrier_code']);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @throws NumberParseException
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public function parse(string $phoneNumber): PhoneNumber
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $sanitizedPhoneNumber = $phoneNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (str_starts_with($sanitizedPhoneNumber, '00')) {
 | 
				
			||||||
 | 
					            $sanitizedPhoneNumber = '+'.substr($sanitizedPhoneNumber, 2, null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!str_starts_with($sanitizedPhoneNumber, '+') && !str_starts_with($sanitizedPhoneNumber, '0')) {
 | 
				
			||||||
 | 
					            $sanitizedPhoneNumber = '+'.$sanitizedPhoneNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $this->phoneNumberUtil->parse($sanitizedPhoneNumber, $this->config['default_carrier_code']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get type (mobile, landline, ...) for phone number.
 | 
					     * Get type (mobile, landline, ...) for phone number.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -104,7 +122,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function isValidPhonenumberAny($phonenumber): bool
 | 
					    public function isValidPhonenumberAny($phonenumber): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (false === $this->isPhonenumberValidationConfigured()) {
 | 
					        if (false === $this->isConfigured) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        $validation = $this->performTwilioLookup($phonenumber);
 | 
					        $validation = $this->performTwilioLookup($phonenumber);
 | 
				
			||||||
@@ -124,7 +142,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function isValidPhonenumberLandOrVoip($phonenumber): bool
 | 
					    public function isValidPhonenumberLandOrVoip($phonenumber): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (false === $this->isPhonenumberValidationConfigured()) {
 | 
					        if (false === $this->isConfigured) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -145,7 +163,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public function isValidPhonenumberMobile($phonenumber): bool
 | 
					    public function isValidPhonenumberMobile($phonenumber): bool
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (false === $this->isPhonenumberValidationConfigured()) {
 | 
					        if (false === $this->isConfigured) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -160,7 +178,7 @@ final class PhonenumberHelper implements PhoneNumberHelperInterface
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private function performTwilioLookup($phonenumber)
 | 
					    private function performTwilioLookup($phonenumber)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if (false === $this->isPhonenumberValidationConfigured()) {
 | 
					        if (false === $this->isConfigured) {
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,15 +13,15 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const dateToISO = (date: Date | null): string | null => {
 | 
					export const dateToISO = (date: Date | null): string | null => {
 | 
				
			||||||
    if (null === date) {
 | 
					  if (null === date) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return [
 | 
					  return [
 | 
				
			||||||
        date.getFullYear(),
 | 
					    date.getFullYear(),
 | 
				
			||||||
        (date.getMonth() + 1).toString().padStart(2, "0"),
 | 
					    (date.getMonth() + 1).toString().padStart(2, "0"),
 | 
				
			||||||
        date.getDate().toString().padStart(2, "0"),
 | 
					    date.getDate().toString().padStart(2, "0"),
 | 
				
			||||||
    ].join("-");
 | 
					  ].join("-");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -30,16 +30,16 @@ export const dateToISO = (date: Date | null): string | null => {
 | 
				
			|||||||
 * **Experimental**
 | 
					 * **Experimental**
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const ISOToDate = (str: string | null): Date | null => {
 | 
					export const ISOToDate = (str: string | null): Date | null => {
 | 
				
			||||||
    if (null === str) {
 | 
					  if (null === str) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    if ("" === str.trim()) {
 | 
					  if ("" === str.trim()) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [year, month, day] = str.split("-").map((p) => parseInt(p));
 | 
					  const [year, month, day] = str.split("-").map((p) => parseInt(p));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new Date(year, month - 1, day, 0, 0, 0, 0);
 | 
					  return new Date(year, month - 1, day, 0, 0, 0, 0);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -47,21 +47,19 @@ export const ISOToDate = (str: string | null): Date | null => {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const ISOToDatetime = (str: string | null): Date | null => {
 | 
					export const ISOToDatetime = (str: string | null): Date | null => {
 | 
				
			||||||
    if (null === str) {
 | 
					  if (null === str) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [cal, times] = str.split("T"),
 | 
					  const [cal, times] = str.split("T"),
 | 
				
			||||||
        [year, month, date] = cal.split("-").map((s) => parseInt(s)),
 | 
					    [year, month, date] = cal.split("-").map((s) => parseInt(s)),
 | 
				
			||||||
        [time, timezone] = times.split(times.charAt(8)),
 | 
					    [time, timezone] = times.split(times.charAt(8)),
 | 
				
			||||||
        [hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
 | 
					    [hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
 | 
				
			||||||
    if ("0000" === timezone) {
 | 
					  if ("0000" === timezone) {
 | 
				
			||||||
        return new Date(
 | 
					    return new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
 | 
				
			||||||
            Date.UTC(year, month - 1, date, hours, minutes, seconds),
 | 
					  }
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new Date(year, month - 1, date, hours, minutes, seconds);
 | 
					  return new Date(year, month - 1, date, hours, minutes, seconds);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -69,96 +67,109 @@ export const ISOToDatetime = (str: string | null): Date | null => {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const datetimeToISO = (date: Date): string => {
 | 
					export const datetimeToISO = (date: Date): string => {
 | 
				
			||||||
    let cal, time, offset;
 | 
					  let cal, time, offset;
 | 
				
			||||||
    cal = [
 | 
					  cal = [
 | 
				
			||||||
        date.getFullYear(),
 | 
					    date.getFullYear(),
 | 
				
			||||||
        (date.getMonth() + 1).toString().padStart(2, "0"),
 | 
					    (date.getMonth() + 1).toString().padStart(2, "0"),
 | 
				
			||||||
        date.getDate().toString().padStart(2, "0"),
 | 
					    date.getDate().toString().padStart(2, "0"),
 | 
				
			||||||
    ].join("-");
 | 
					  ].join("-");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    time = [
 | 
					  time = [
 | 
				
			||||||
        date.getHours().toString().padStart(2, "0"),
 | 
					    date.getHours().toString().padStart(2, "0"),
 | 
				
			||||||
        date.getMinutes().toString().padStart(2, "0"),
 | 
					    date.getMinutes().toString().padStart(2, "0"),
 | 
				
			||||||
        date.getSeconds().toString().padStart(2, "0"),
 | 
					    date.getSeconds().toString().padStart(2, "0"),
 | 
				
			||||||
    ].join(":");
 | 
					  ].join(":");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    offset = [
 | 
					  offset = [
 | 
				
			||||||
        date.getTimezoneOffset() <= 0 ? "+" : "-",
 | 
					    date.getTimezoneOffset() <= 0 ? "+" : "-",
 | 
				
			||||||
        Math.abs(Math.floor(date.getTimezoneOffset() / 60))
 | 
					    Math.abs(Math.floor(date.getTimezoneOffset() / 60))
 | 
				
			||||||
            .toString()
 | 
					      .toString()
 | 
				
			||||||
            .padStart(2, "0"),
 | 
					      .padStart(2, "0"),
 | 
				
			||||||
        ":",
 | 
					    ":",
 | 
				
			||||||
        Math.abs(date.getTimezoneOffset() % 60)
 | 
					    Math.abs(date.getTimezoneOffset() % 60)
 | 
				
			||||||
            .toString()
 | 
					      .toString()
 | 
				
			||||||
            .padStart(2, "0"),
 | 
					      .padStart(2, "0"),
 | 
				
			||||||
    ].join("");
 | 
					  ].join("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const x = cal + "T" + time + offset;
 | 
					  const x = cal + "T" + time + offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return x;
 | 
					  return x;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const intervalDaysToISO = (days: number | string | null): string => {
 | 
					export const intervalDaysToISO = (days: number | string | null): string => {
 | 
				
			||||||
    if (null === days) {
 | 
					  if (null === days) {
 | 
				
			||||||
        return "P0D";
 | 
					    return "P0D";
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `P${days}D`;
 | 
					  return `P${days}D`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const intervalISOToDays = (str: string | null): number | null => {
 | 
					export const intervalISOToDays = (str: string | null): number | null => {
 | 
				
			||||||
    if (null === str) {
 | 
					  if (null === str) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ("" === str.trim()) {
 | 
					  if ("" === str.trim()) {
 | 
				
			||||||
        return null;
 | 
					    return null;
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let days = 0;
 | 
					  let days = 0;
 | 
				
			||||||
    let isDate = true;
 | 
					  let isDate = true;
 | 
				
			||||||
    let vstring = "";
 | 
					  let vstring = "";
 | 
				
			||||||
    for (let i = 0; i < str.length; i = i + 1) {
 | 
					  for (let i = 0; i < str.length; i = i + 1) {
 | 
				
			||||||
        if (!isDate) {
 | 
					    if (!isDate) {
 | 
				
			||||||
            continue;
 | 
					      continue;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        switch (str.charAt(i)) {
 | 
					 | 
				
			||||||
            case "P":
 | 
					 | 
				
			||||||
                isDate = true;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "T":
 | 
					 | 
				
			||||||
                isDate = false;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "0":
 | 
					 | 
				
			||||||
            case "1":
 | 
					 | 
				
			||||||
            case "2":
 | 
					 | 
				
			||||||
            case "3":
 | 
					 | 
				
			||||||
            case "4":
 | 
					 | 
				
			||||||
            case "5":
 | 
					 | 
				
			||||||
            case "6":
 | 
					 | 
				
			||||||
            case "7":
 | 
					 | 
				
			||||||
            case "8":
 | 
					 | 
				
			||||||
            case "9":
 | 
					 | 
				
			||||||
                vstring = vstring + str.charAt(i);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "Y":
 | 
					 | 
				
			||||||
                days = days + Number.parseInt(vstring) * 365;
 | 
					 | 
				
			||||||
                vstring = "";
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "M":
 | 
					 | 
				
			||||||
                days = days + Number.parseInt(vstring) * 30;
 | 
					 | 
				
			||||||
                vstring = "";
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case "D":
 | 
					 | 
				
			||||||
                days = days + Number.parseInt(vstring);
 | 
					 | 
				
			||||||
                vstring = "";
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            default:
 | 
					 | 
				
			||||||
                throw Error(
 | 
					 | 
				
			||||||
                    "this character should not appears: " + str.charAt(i),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    switch (str.charAt(i)) {
 | 
				
			||||||
 | 
					      case "P":
 | 
				
			||||||
 | 
					        isDate = true;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case "T":
 | 
				
			||||||
 | 
					        isDate = false;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case "0":
 | 
				
			||||||
 | 
					      case "1":
 | 
				
			||||||
 | 
					      case "2":
 | 
				
			||||||
 | 
					      case "3":
 | 
				
			||||||
 | 
					      case "4":
 | 
				
			||||||
 | 
					      case "5":
 | 
				
			||||||
 | 
					      case "6":
 | 
				
			||||||
 | 
					      case "7":
 | 
				
			||||||
 | 
					      case "8":
 | 
				
			||||||
 | 
					      case "9":
 | 
				
			||||||
 | 
					        vstring = vstring + str.charAt(i);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case "Y":
 | 
				
			||||||
 | 
					        days = days + Number.parseInt(vstring) * 365;
 | 
				
			||||||
 | 
					        vstring = "";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case "M":
 | 
				
			||||||
 | 
					        days = days + Number.parseInt(vstring) * 30;
 | 
				
			||||||
 | 
					        vstring = "";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case "D":
 | 
				
			||||||
 | 
					        days = days + Number.parseInt(vstring);
 | 
				
			||||||
 | 
					        vstring = "";
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        throw Error("this character should not appears: " + str.charAt(i));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return days;
 | 
					  return days;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getTimezoneOffsetString(date: Date, timeZone: string): string {
 | 
				
			||||||
 | 
					  const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
 | 
				
			||||||
 | 
					  const tzDate  = new Date(date.toLocaleString("en-US", { timeZone }));
 | 
				
			||||||
 | 
					  const offsetMinutes = (utcDate.getTime() - tzDate.getTime()) / (60 * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Inverser le signe pour avoir la convention ±HH:MM
 | 
				
			||||||
 | 
					  const sign = offsetMinutes <= 0 ? "+" : "-";
 | 
				
			||||||
 | 
					  const absMinutes = Math.abs(offsetMinutes);
 | 
				
			||||||
 | 
					  const hours = String(Math.floor(absMinutes / 60)).padStart(2, "0");
 | 
				
			||||||
 | 
					  const minutes = String(absMinutes % 60).padStart(2, "0");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return `${sign}${hours}:${minutes}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,6 @@ $chill-theme-buttons: (
 | 
				
			|||||||
   &.btn-unlink,
 | 
					   &.btn-unlink,
 | 
				
			||||||
   &.btn-action,
 | 
					   &.btn-action,
 | 
				
			||||||
   &.btn-edit,
 | 
					   &.btn-edit,
 | 
				
			||||||
   &.btn-tpchild,
 | 
					 | 
				
			||||||
   &.btn-wopilink,
 | 
					   &.btn-wopilink,
 | 
				
			||||||
   &.btn-update {
 | 
					   &.btn-update {
 | 
				
			||||||
       &, &:hover {
 | 
					       &, &:hover {
 | 
				
			||||||
@@ -82,7 +81,6 @@ $chill-theme-buttons: (
 | 
				
			|||||||
   &.btn-remove::before,
 | 
					   &.btn-remove::before,
 | 
				
			||||||
   &.btn-choose::before,
 | 
					   &.btn-choose::before,
 | 
				
			||||||
   &.btn-notify::before,
 | 
					   &.btn-notify::before,
 | 
				
			||||||
   &.btn-tpchild::before,
 | 
					 | 
				
			||||||
   &.btn-download::before,
 | 
					   &.btn-download::before,
 | 
				
			||||||
   &.btn-search::before,
 | 
					   &.btn-search::before,
 | 
				
			||||||
   &.btn-cancel::before {
 | 
					   &.btn-cancel::before {
 | 
				
			||||||
@@ -112,7 +110,6 @@ $chill-theme-buttons: (
 | 
				
			|||||||
   &.btn-choose::before    { content: "\f00c"; } // fa-check  // f046 fa-check-square-o
 | 
					   &.btn-choose::before    { content: "\f00c"; } // fa-check  // f046 fa-check-square-o
 | 
				
			||||||
   &.btn-unlink::before    { content: "\f127"; } // fa-chain-broken
 | 
					   &.btn-unlink::before    { content: "\f127"; } // fa-chain-broken
 | 
				
			||||||
   &.btn-notify::before    { content: "\f1d8"; } // fa-paper-plane
 | 
					   &.btn-notify::before    { content: "\f1d8"; } // fa-paper-plane
 | 
				
			||||||
   &.btn-tpchild::before   { content: "\f007"; } // fa-user
 | 
					 | 
				
			||||||
   &.btn-download::before  { content: "\f019"; } // fa-download
 | 
					   &.btn-download::before  { content: "\f019"; } // fa-download
 | 
				
			||||||
   &.btn-search::before    { content: "\f002"; } // fa-search
 | 
					   &.btn-search::before    { content: "\f002"; } // fa-search
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,61 +1,61 @@
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
    Address,
 | 
					  Address,
 | 
				
			||||||
    GeographicalUnitLayer,
 | 
					  GeographicalUnitLayer,
 | 
				
			||||||
    SimpleGeographicalUnit,
 | 
					  SimpleGeographicalUnit,
 | 
				
			||||||
} from "../../types";
 | 
					} from "../../types";
 | 
				
			||||||
import { fetchResults, makeFetch } from "./apiMethods";
 | 
					import { fetchResults, makeFetch } from "./apiMethods";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAddressById = async (address_id: number): Promise<Address> => {
 | 
					export const getAddressById = async (address_id: number): Promise<Address> => {
 | 
				
			||||||
    const url = `/api/1.0/main/address/${address_id}.json`;
 | 
					  const url = `/api/1.0/main/address/${address_id}.json`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const response = await fetch(url);
 | 
					  const response = await fetch(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (response.ok) {
 | 
					  if (response.ok) {
 | 
				
			||||||
        return response.json();
 | 
					    return response.json();
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    throw Error("Error with request resource response");
 | 
					  throw Error("Error with request resource response");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getGeographicalUnitsByAddress = async (
 | 
					export const getGeographicalUnitsByAddress = async (
 | 
				
			||||||
    address: Address,
 | 
					  address: Address,
 | 
				
			||||||
): Promise<SimpleGeographicalUnit[]> => {
 | 
					): Promise<SimpleGeographicalUnit[]> => {
 | 
				
			||||||
    return fetchResults<SimpleGeographicalUnit>(
 | 
					  return fetchResults<SimpleGeographicalUnit>(
 | 
				
			||||||
        `/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`,
 | 
					    `/api/1.0/main/geographical-unit/by-address/${address.address_id}.json`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAllGeographicalUnitLayers = async (): Promise<
 | 
					export const getAllGeographicalUnitLayers = async (): Promise<
 | 
				
			||||||
    GeographicalUnitLayer[]
 | 
					  GeographicalUnitLayer[]
 | 
				
			||||||
> => {
 | 
					> => {
 | 
				
			||||||
    return fetchResults<GeographicalUnitLayer>(
 | 
					  return fetchResults<GeographicalUnitLayer>(
 | 
				
			||||||
        `/api/1.0/main/geographical-unit-layer.json`,
 | 
					    `/api/1.0/main/geographical-unit-layer.json`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const syncAddressWithReference = async (
 | 
					export const syncAddressWithReference = async (
 | 
				
			||||||
    address: Address,
 | 
					  address: Address,
 | 
				
			||||||
): Promise<Address> => {
 | 
					): Promise<Address> => {
 | 
				
			||||||
    return makeFetch<null, Address>(
 | 
					  return makeFetch<null, Address>(
 | 
				
			||||||
        "POST",
 | 
					    "POST",
 | 
				
			||||||
        `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`,
 | 
					    `/api/1.0/main/address/reference-match/${address.address_id}/sync-with-reference`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const markAddressReviewed = async (
 | 
					export const markAddressReviewed = async (
 | 
				
			||||||
    address: Address,
 | 
					  address: Address,
 | 
				
			||||||
): Promise<Address> => {
 | 
					): Promise<Address> => {
 | 
				
			||||||
    return makeFetch<null, Address>(
 | 
					  return makeFetch<null, Address>(
 | 
				
			||||||
        "POST",
 | 
					    "POST",
 | 
				
			||||||
        `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`,
 | 
					    `/api/1.0/main/address/reference-match/${address.address_id}/set/reviewed`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const markAddressToReview = async (
 | 
					export const markAddressToReview = async (
 | 
				
			||||||
    address: Address,
 | 
					  address: Address,
 | 
				
			||||||
): Promise<Address> => {
 | 
					): Promise<Address> => {
 | 
				
			||||||
    return makeFetch<null, Address>(
 | 
					  return makeFetch<null, Address>(
 | 
				
			||||||
        "POST",
 | 
					    "POST",
 | 
				
			||||||
        `/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`,
 | 
					    `/api/1.0/main/address/reference-match/${address.address_id}/set/to_review`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,288 +1,521 @@
 | 
				
			|||||||
import { Scope } from "../../types";
 | 
					import {
 | 
				
			||||||
 | 
					  DynamicKeys,
 | 
				
			||||||
 | 
					  Scope,
 | 
				
			||||||
 | 
					  ValidationExceptionInterface,
 | 
				
			||||||
 | 
					  ValidationProblemFromMap,
 | 
				
			||||||
 | 
					  ViolationFromMap
 | 
				
			||||||
 | 
					} from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type body = Record<string, boolean | string | number | null>;
 | 
					export type body = Record<string, boolean | string | number | null>;
 | 
				
			||||||
export type fetchOption = Record<string, boolean | string | number | null>;
 | 
					export type fetchOption = Record<string, boolean | string | number | null>;
 | 
				
			||||||
 | 
					export type Primitive = string | number | boolean | null;
 | 
				
			||||||
export type Params = Record<string, number | string>;
 | 
					export type Params = Record<string, number | string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Pagination {
 | 
				
			||||||
 | 
					  first: number;
 | 
				
			||||||
 | 
					  items_per_page: number;
 | 
				
			||||||
 | 
					  more: boolean;
 | 
				
			||||||
 | 
					  next: string | null;
 | 
				
			||||||
 | 
					  previous: string | null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PaginationResponse<T> {
 | 
					export interface PaginationResponse<T> {
 | 
				
			||||||
    pagination: {
 | 
					  pagination: Pagination;
 | 
				
			||||||
        more: boolean;
 | 
					  results: T[];
 | 
				
			||||||
        items_per_page: number;
 | 
					  count: number;
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    results: T[];
 | 
					 | 
				
			||||||
    count: number;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type FetchParams = Record<string, string | number | null>;
 | 
					export type FetchParams = Record<string, string | number | null>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TransportExceptionInterface {
 | 
					export interface TransportExceptionInterface {
 | 
				
			||||||
    name: string;
 | 
					  name: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ValidationExceptionInterface
 | 
					export class ValidationException<
 | 
				
			||||||
    extends TransportExceptionInterface {
 | 
					    M extends Record<string, Record<string, string|number>> = Record<
 | 
				
			||||||
    name: "ValidationException";
 | 
					      string,
 | 
				
			||||||
    error: object;
 | 
					      Record<string, string|number>
 | 
				
			||||||
    violations: string[];
 | 
					    >,
 | 
				
			||||||
    titles: string[];
 | 
					  >
 | 
				
			||||||
    propertyPaths: string[];
 | 
					  extends Error
 | 
				
			||||||
}
 | 
					  implements ValidationExceptionInterface<M>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public readonly name = "ValidationException" as const;
 | 
				
			||||||
 | 
					  public readonly problems: ValidationProblemFromMap<M>;
 | 
				
			||||||
 | 
					  public readonly violations: string[];
 | 
				
			||||||
 | 
					  public readonly violationsList: ViolationFromMap<M>[];
 | 
				
			||||||
 | 
					  public readonly titles: string[];
 | 
				
			||||||
 | 
					  public readonly propertyPaths: DynamicKeys<M> & string[];
 | 
				
			||||||
 | 
					  public readonly byProperty: Record<Extract<keyof M, string>, string[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ValidationErrorResponse extends TransportExceptionInterface {
 | 
					  constructor(problem: ValidationProblemFromMap<M>) {
 | 
				
			||||||
    violations: {
 | 
					    const message = [problem.title, problem.detail].filter(Boolean).join(" — ");
 | 
				
			||||||
        title: string;
 | 
					    super(message);
 | 
				
			||||||
        propertyPath: string;
 | 
					    Object.setPrototypeOf(this, new.target.prototype);
 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface AccessExceptionInterface extends TransportExceptionInterface {
 | 
					    this.problems = problem;
 | 
				
			||||||
    name: "AccessException";
 | 
					 | 
				
			||||||
    violations: string[];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface NotFoundExceptionInterface
 | 
					    this.violationsList = problem.violations;
 | 
				
			||||||
    extends TransportExceptionInterface {
 | 
					    this.violations = problem.violations.map(
 | 
				
			||||||
    name: "NotFoundException";
 | 
					      (v) => `${v.title}: ${v.propertyPath}`,
 | 
				
			||||||
}
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ServerExceptionInterface extends TransportExceptionInterface {
 | 
					    this.titles = problem.violations.map((v) => v.title);
 | 
				
			||||||
    name: "ServerException";
 | 
					 | 
				
			||||||
    message: string;
 | 
					 | 
				
			||||||
    code: number;
 | 
					 | 
				
			||||||
    body: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ConflictHttpExceptionInterface
 | 
					    this.propertyPaths = problem.violations.map(
 | 
				
			||||||
    extends TransportExceptionInterface {
 | 
					      (v) => v.propertyPath,
 | 
				
			||||||
    name: "ConflictHttpException";
 | 
					    ) as DynamicKeys<M> & string[];
 | 
				
			||||||
    violations: string[];
 | 
					
 | 
				
			||||||
 | 
					    this.byProperty = problem.violations.reduce(
 | 
				
			||||||
 | 
					      (acc, v) => {
 | 
				
			||||||
 | 
					        const key = v.propertyPath.replace('/\[\d+\]$/', "") as Extract<keyof M, string>;
 | 
				
			||||||
 | 
					        (acc[key] ||= []).push(v.title);
 | 
				
			||||||
 | 
					        return acc;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {} as Record<Extract<keyof M, string>, string[]>,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (Error.captureStackTrace) {
 | 
				
			||||||
 | 
					      Error.captureStackTrace(this, ValidationException);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  violationsByNormalizedProperty(property: Extract<keyof M, string>): ViolationFromMap<M>[] {
 | 
				
			||||||
 | 
					    return this.violationsList.filter((v) => v.propertyPath.replace(/\[\d+\]$/, "") === property);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  violationsByNormalizedPropertyAndParams<
 | 
				
			||||||
 | 
					    P extends Extract<keyof M, string>,
 | 
				
			||||||
 | 
					    K extends Extract<keyof M[P], string>
 | 
				
			||||||
 | 
					  >(
 | 
				
			||||||
 | 
					    property: P,
 | 
				
			||||||
 | 
					    param: K,
 | 
				
			||||||
 | 
					    param_value: M[P][K]
 | 
				
			||||||
 | 
					  ): ViolationFromMap<M>[]
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    const list = this.violationsByNormalizedProperty(property);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return  list.filter(
 | 
				
			||||||
 | 
					      (v): boolean =>
 | 
				
			||||||
 | 
					        !!v.parameters &&
 | 
				
			||||||
 | 
					        // `with_parameter in v.parameters` check indexing
 | 
				
			||||||
 | 
					        param in v.parameters &&
 | 
				
			||||||
 | 
					        // the cast is safe, because we have overloading that bind the types
 | 
				
			||||||
 | 
					        (v.parameters as M[P])[param] === param_value
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Generic api method that can be adapted to any fetch request
 | 
					 * Check that the exception is a ValidationExceptionInterface
 | 
				
			||||||
 *
 | 
					 * @param x
 | 
				
			||||||
 * This method is suitable make a single fetch. When performing a GET to fetch a list of elements, always consider pagination
 | 
					 | 
				
			||||||
 * and use of the @link{fetchResults} method.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const makeFetch = <Input, Output>(
 | 
					export function isValidationException<M extends Record<string, Record<string, string|number>>>(
 | 
				
			||||||
    method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE",
 | 
					  x: unknown,
 | 
				
			||||||
    url: string,
 | 
					): x is ValidationExceptionInterface<M> {
 | 
				
			||||||
    body?: body | Input | null,
 | 
					  return (
 | 
				
			||||||
    options?: FetchParams,
 | 
					    x instanceof ValidationException ||
 | 
				
			||||||
 | 
					    (typeof x === "object" &&
 | 
				
			||||||
 | 
					      x !== null &&
 | 
				
			||||||
 | 
					      (x as any).name === "ValidationException")
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isValidationProblem(x: unknown): x is {
 | 
				
			||||||
 | 
					  type: string;
 | 
				
			||||||
 | 
					  title: string;
 | 
				
			||||||
 | 
					  violations: { propertyPath: string; title: string }[];
 | 
				
			||||||
 | 
					} {
 | 
				
			||||||
 | 
					  if (!x || typeof x !== "object") return false;
 | 
				
			||||||
 | 
					  const o = x as any;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    typeof o.type === "string" &&
 | 
				
			||||||
 | 
					    typeof o.title === "string" &&
 | 
				
			||||||
 | 
					    Array.isArray(o.violations) &&
 | 
				
			||||||
 | 
					    o.violations.every(
 | 
				
			||||||
 | 
					      (v: any) =>
 | 
				
			||||||
 | 
					        v &&
 | 
				
			||||||
 | 
					        typeof v === "object" &&
 | 
				
			||||||
 | 
					        typeof v.propertyPath === "string" &&
 | 
				
			||||||
 | 
					        typeof v.title === "string",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface AccessExceptionInterface extends TransportExceptionInterface {
 | 
				
			||||||
 | 
					  name: "AccessException";
 | 
				
			||||||
 | 
					  violations: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface NotFoundExceptionInterface
 | 
				
			||||||
 | 
					  extends TransportExceptionInterface {
 | 
				
			||||||
 | 
					  name: "NotFoundException";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ServerExceptionInterface extends TransportExceptionInterface {
 | 
				
			||||||
 | 
					  name: "ServerException";
 | 
				
			||||||
 | 
					  message: string;
 | 
				
			||||||
 | 
					  code: number;
 | 
				
			||||||
 | 
					  body: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface ConflictHttpExceptionInterface
 | 
				
			||||||
 | 
					  extends TransportExceptionInterface {
 | 
				
			||||||
 | 
					  name: "ConflictHttpException";
 | 
				
			||||||
 | 
					  violations: string[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Generic api method that can be adapted to any fetch request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * What this does
 | 
				
			||||||
 | 
					 * - Performs a single HTTP request using fetch and returns the parsed JSON as Output.
 | 
				
			||||||
 | 
					 * - Interprets common API errors and throws typed exceptions you can catch in your UI.
 | 
				
			||||||
 | 
					 * - When the server returns a Symfony validation problem (HTTP 422), the error is
 | 
				
			||||||
 | 
					 *   rethrown as a typed ValidationException that is aware of your Violation Map (see below).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Important: For GET endpoints that return lists, prefer using fetchResults, which
 | 
				
			||||||
 | 
					 * handles pagination and aggregation for you.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Violation Map (M): make your 422 errors strongly typed
 | 
				
			||||||
 | 
					 * ------------------------------------------------------
 | 
				
			||||||
 | 
					 * Symfony’s validation problem+json payload looks like this (simplified):
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   {
 | 
				
			||||||
 | 
					 *     "type": "https://symfony.com/errors/validation",
 | 
				
			||||||
 | 
					 *     "title": "Validation Failed",
 | 
				
			||||||
 | 
					 *     "violations": [
 | 
				
			||||||
 | 
					 *       {
 | 
				
			||||||
 | 
					 *         "propertyPath": "mobilenumber",
 | 
				
			||||||
 | 
					 *         "title": "This value is not a valid phone number.",
 | 
				
			||||||
 | 
					 *         "parameters": {
 | 
				
			||||||
 | 
					 *           "{{ value }}": "+33 1 02 03 04 05",
 | 
				
			||||||
 | 
					 *           "{{ types }}": "mobile number"
 | 
				
			||||||
 | 
					 *         },
 | 
				
			||||||
 | 
					 *         "type": "urn:uuid:..."
 | 
				
			||||||
 | 
					 *       }
 | 
				
			||||||
 | 
					 *     ]
 | 
				
			||||||
 | 
					 *   }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The makeFetch generic type parameter M lets you describe, field by field, which
 | 
				
			||||||
 | 
					 * parameters may appear for each propertyPath. Doing so gives you full type-safety when
 | 
				
			||||||
 | 
					 * consuming ValidationException in your UI code.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * How to build M (Violation Map)
 | 
				
			||||||
 | 
					 * - M is a map where each key is a server-side propertyPath (string), and the value is a
 | 
				
			||||||
 | 
					 *   record describing the allowed keys in the parameters object for that property.
 | 
				
			||||||
 | 
					 * - Keys in parameters are the exact strings you receive from Symfony, including the
 | 
				
			||||||
 | 
					 *   curly-braced placeholders such as "{{ value }}", "{{ types }}", etc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Example from Person creation (WritePersonViolationMap)
 | 
				
			||||||
 | 
					 * -----------------------------------------------------
 | 
				
			||||||
 | 
					 * In ChillPersonBundle/Resources/public/vuejs/_api/OnTheFly.ts you’ll find:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   export type WritePersonViolationMap = {
 | 
				
			||||||
 | 
					 *     gender: {
 | 
				
			||||||
 | 
					 *       "{{ value }}": string | null;
 | 
				
			||||||
 | 
					 *     };
 | 
				
			||||||
 | 
					 *     mobilenumber: {
 | 
				
			||||||
 | 
					 *       "{{ types }}": string;          // ex: "mobile number"
 | 
				
			||||||
 | 
					 *       "{{ value }}": string;          // ex: "+33 1 02 03 04 05"
 | 
				
			||||||
 | 
					 *     };
 | 
				
			||||||
 | 
					 *   };
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This means:
 | 
				
			||||||
 | 
					 * - If the server reports a violation for propertyPath "gender", the parameters object
 | 
				
			||||||
 | 
					 *   is expected to contain a key "{{ value }}" with a string or null value.
 | 
				
			||||||
 | 
					 * - If the server reports a violation for propertyPath "mobilenumber", the parameters
 | 
				
			||||||
 | 
					 *   may include "{{ value }}" and "{{ types }}" as strings.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * How makeFetch uses M
 | 
				
			||||||
 | 
					 * - When the response has status 422 and the payload matches a Symfony validation
 | 
				
			||||||
 | 
					 *   problem, makeFetch casts it to ValidationProblemFromMap<M> and throws a
 | 
				
			||||||
 | 
					 *   ValidationException<M>.
 | 
				
			||||||
 | 
					 * - The ValidationException exposes helpful, pre-computed fields:
 | 
				
			||||||
 | 
					 *   - exception.problem: the full typed payload
 | 
				
			||||||
 | 
					 *   - exception.violations: ["Title: propertyPath", ...]
 | 
				
			||||||
 | 
					 *   - exception.titles: ["Title 1", "Title 2", ...]
 | 
				
			||||||
 | 
					 *   - exception.propertyPaths: ["gender", "mobilenumber", ...] (typed from M)
 | 
				
			||||||
 | 
					 *   - exception.byProperty: { gender: [titles...], mobilenumber: [titles...] }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Typical usage patterns
 | 
				
			||||||
 | 
					 * ----------------------
 | 
				
			||||||
 | 
					 * 1) GET without Validation Map (no 422 expected):
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   const centers = await makeFetch<null, { showCenters: boolean; centers: Center[] }>(
 | 
				
			||||||
 | 
					 *     "GET",
 | 
				
			||||||
 | 
					 *     "/api/1.0/person/creation/authorized-centers",
 | 
				
			||||||
 | 
					 *     null
 | 
				
			||||||
 | 
					 *   );
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 2) POST with body and Violation Map:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   type WritePersonViolationMap = {
 | 
				
			||||||
 | 
					 *     gender: { "{{ value }}": string | null };
 | 
				
			||||||
 | 
					 *     mobilenumber: { "{{ types }}": string; "{{ value }}": string };
 | 
				
			||||||
 | 
					 *   };
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   try {
 | 
				
			||||||
 | 
					 *     const created = await makeFetch<PersonWrite, Person, WritePersonViolationMap>(
 | 
				
			||||||
 | 
					 *       "POST",
 | 
				
			||||||
 | 
					 *       "/api/1.0/person/person.json",
 | 
				
			||||||
 | 
					 *       personPayload
 | 
				
			||||||
 | 
					 *     );
 | 
				
			||||||
 | 
					 *     // Success path
 | 
				
			||||||
 | 
					 *   } catch (e) {
 | 
				
			||||||
 | 
					 *     if (isValidationException(e)) {
 | 
				
			||||||
 | 
					 *       // Fully typed:
 | 
				
			||||||
 | 
					 *       e.propertyPaths.includes("mobilenumber");
 | 
				
			||||||
 | 
					 *       const firstTitleForMobile = e.byProperty.mobilenumber?.[0];
 | 
				
			||||||
 | 
					 *       // You can also inspect parameter values:
 | 
				
			||||||
 | 
					 *       const v = e.problem.violations.find(v => v.propertyPath === "mobilenumber");
 | 
				
			||||||
 | 
					 *       const rawValue = v?.parameters?.["{{ value }}"]; // typed as string
 | 
				
			||||||
 | 
					 *     } else {
 | 
				
			||||||
 | 
					 *       // Other error handling (AccessException, ConflictHttpException, etc.)
 | 
				
			||||||
 | 
					 *     }
 | 
				
			||||||
 | 
					 *   }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Tips to design your Violation Map
 | 
				
			||||||
 | 
					 * - Use exact propertyPath strings as exposed by the API (they usually match your
 | 
				
			||||||
 | 
					 *   DTO field names or entity property paths used by the validator).
 | 
				
			||||||
 | 
					 * - Inside each property, list only the placeholders that you actually read in the UI
 | 
				
			||||||
 | 
					 *   (you can always add more later). This keeps your types strict but pragmatic.
 | 
				
			||||||
 | 
					 * - If a field may not include parameters at all, you can set it to an empty object {}.
 | 
				
			||||||
 | 
					 * - If you don’t care about parameter typing, you can omit M entirely and rely on the
 | 
				
			||||||
 | 
					 *   default loose typing (Record<string, Primitive>), but you’ll lose safety.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Error taxonomy thrown by makeFetch
 | 
				
			||||||
 | 
					 * - ValidationException<M> when status = 422 and payload is a validation problem.
 | 
				
			||||||
 | 
					 * - AccessException when status = 403.
 | 
				
			||||||
 | 
					 * - ConflictHttpException when status = 409.
 | 
				
			||||||
 | 
					 * - A generic error object for other non-ok statuses.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @typeParam Input  - Shape of the request body you send (if any)
 | 
				
			||||||
 | 
					 * @typeParam Output - Shape of the successful JSON response you expect
 | 
				
			||||||
 | 
					 * @typeParam M      - Violation Map describing the per-field parameters you expect
 | 
				
			||||||
 | 
					 *                     in Symfony validation violations. See examples above.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param method  The HTTP method to use (POST, GET, PUT, PATCH, DELETE)
 | 
				
			||||||
 | 
					 * @param url     The absolute or relative URL to call
 | 
				
			||||||
 | 
					 * @param body    The request payload. If null/undefined, no body is sent
 | 
				
			||||||
 | 
					 * @param options Extra fetch options/headers merged into the request
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @returns The parsed JSON response typed as Output. For 204 No Content, resolves
 | 
				
			||||||
 | 
					 *          with undefined (void).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const makeFetch = async <
 | 
				
			||||||
 | 
					  Input,
 | 
				
			||||||
 | 
					  Output,
 | 
				
			||||||
 | 
					  M extends Record<string, Record<string, string|number>> = Record<
 | 
				
			||||||
 | 
					    string,
 | 
				
			||||||
 | 
					    Record<string, string|number>
 | 
				
			||||||
 | 
					  >,
 | 
				
			||||||
 | 
					>(
 | 
				
			||||||
 | 
					  method: "POST" | "GET" | "PUT" | "PATCH" | "DELETE",
 | 
				
			||||||
 | 
					  url: string,
 | 
				
			||||||
 | 
					  body?: body | Input | null,
 | 
				
			||||||
 | 
					  options?: FetchParams,
 | 
				
			||||||
): Promise<Output> => {
 | 
					): Promise<Output> => {
 | 
				
			||||||
    let opts = {
 | 
					  let opts = {
 | 
				
			||||||
        method: method,
 | 
					    method: method,
 | 
				
			||||||
        headers: {
 | 
					    headers: {
 | 
				
			||||||
            "Content-Type": "application/json;charset=utf-8",
 | 
					      "Content-Type": "application/json;charset=utf-8",
 | 
				
			||||||
        },
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (body !== null && typeof body !== "undefined") {
 | 
				
			||||||
 | 
					    Object.assign(opts, { body: JSON.stringify(body) });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (typeof options !== "undefined") {
 | 
				
			||||||
 | 
					    opts = Object.assign(opts, options);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url, opts).then(async (response) => {
 | 
				
			||||||
 | 
					    if (response.status === 204) {
 | 
				
			||||||
 | 
					      return Promise.resolve();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (response.ok) {
 | 
				
			||||||
 | 
					      return response.json();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (response.status === 422) {
 | 
				
			||||||
 | 
					      // Unprocessable Entity -> payload de validation Symfony
 | 
				
			||||||
 | 
					      const json = await response.json().catch(() => undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (isValidationProblem(json)) {
 | 
				
			||||||
 | 
					        // On ré-interprète le payload selon M (ParamMap) pour typer les violations
 | 
				
			||||||
 | 
					        const problem = json as unknown as ValidationProblemFromMap<M>;
 | 
				
			||||||
 | 
					        throw new ValidationException<M>(problem);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const err = new Error(
 | 
				
			||||||
 | 
					        "Validation failed but payload is not a ValidationProblem",
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      (err as any).raw = json;
 | 
				
			||||||
 | 
					      throw err;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (response.status === 403) {
 | 
				
			||||||
 | 
					      throw AccessException(response);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (response.status === 409) {
 | 
				
			||||||
 | 
					      throw ConflictHttpException(response);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throw {
 | 
				
			||||||
 | 
					      name: "Exception",
 | 
				
			||||||
 | 
					      sta: response.status,
 | 
				
			||||||
 | 
					      txt: response.statusText,
 | 
				
			||||||
 | 
					      err: new Error(),
 | 
				
			||||||
 | 
					      violations: response.body,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
    if (body !== null && typeof body !== "undefined") {
 | 
					 | 
				
			||||||
        Object.assign(opts, { body: JSON.stringify(body) });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (typeof options !== "undefined") {
 | 
					 | 
				
			||||||
        opts = Object.assign(opts, options);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return fetch(url, opts).then((response) => {
 | 
					 | 
				
			||||||
        if (response.status === 204) {
 | 
					 | 
				
			||||||
            return Promise.resolve();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response.ok) {
 | 
					 | 
				
			||||||
            return response.json();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response.status === 422) {
 | 
					 | 
				
			||||||
            return response.json().then((response) => {
 | 
					 | 
				
			||||||
                throw ValidationException(response);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response.status === 403) {
 | 
					 | 
				
			||||||
            throw AccessException(response);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (response.status === 409) {
 | 
					 | 
				
			||||||
            throw ConflictHttpException(response);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        throw {
 | 
					 | 
				
			||||||
            name: "Exception",
 | 
					 | 
				
			||||||
            sta: response.status,
 | 
					 | 
				
			||||||
            txt: response.statusText,
 | 
					 | 
				
			||||||
            err: new Error(),
 | 
					 | 
				
			||||||
            violations: response.body,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Fetch results with certain parameters
 | 
					 * Fetch results with certain parameters
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function _fetchAction<T>(
 | 
					function _fetchAction<T>(
 | 
				
			||||||
    page: number,
 | 
					  page: number,
 | 
				
			||||||
    uri: string,
 | 
					  uri: string,
 | 
				
			||||||
    params?: FetchParams,
 | 
					  params?: FetchParams,
 | 
				
			||||||
): Promise<PaginationResponse<T>> {
 | 
					): Promise<PaginationResponse<T>> {
 | 
				
			||||||
    const item_per_page = 50;
 | 
					  const item_per_page = 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const searchParams = new URLSearchParams();
 | 
					  const searchParams = new URLSearchParams();
 | 
				
			||||||
    searchParams.append("item_per_page", item_per_page.toString());
 | 
					  searchParams.append("item_per_page", item_per_page.toString());
 | 
				
			||||||
    searchParams.append("page", page.toString());
 | 
					  searchParams.append("page", page.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (params !== undefined) {
 | 
					  if (params !== undefined) {
 | 
				
			||||||
        Object.keys(params).forEach((key) => {
 | 
					    Object.keys(params).forEach((key) => {
 | 
				
			||||||
            const v = params[key];
 | 
					      const v = params[key];
 | 
				
			||||||
            if (typeof v === "string") {
 | 
					      if (typeof v === "string") {
 | 
				
			||||||
                searchParams.append(key, v);
 | 
					        searchParams.append(key, v);
 | 
				
			||||||
            } else if (typeof v === "number") {
 | 
					      } else if (typeof v === "number") {
 | 
				
			||||||
                searchParams.append(key, v.toString());
 | 
					        searchParams.append(key, v.toString());
 | 
				
			||||||
            } else if (v === null) {
 | 
					      } else if (v === null) {
 | 
				
			||||||
                searchParams.append(key, "");
 | 
					        searchParams.append(key, "");
 | 
				
			||||||
            }
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const url = uri + "?" + searchParams.toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fetch(url, {
 | 
				
			||||||
 | 
					    method: "GET",
 | 
				
			||||||
 | 
					    headers: {
 | 
				
			||||||
 | 
					      "Content-Type": "application/json;charset=utf-8",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					    .then((response) => {
 | 
				
			||||||
 | 
					      if (response.ok) {
 | 
				
			||||||
 | 
					        return response.json();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status === 404) {
 | 
				
			||||||
 | 
					        throw NotFoundException(response);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status === 403) {
 | 
				
			||||||
 | 
					        throw AccessException(response);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status >= 500) {
 | 
				
			||||||
 | 
					        return response.text().then((body) => {
 | 
				
			||||||
 | 
					          throw ServerException(response.status, body);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const url = uri + "?" + searchParams.toString();
 | 
					      throw new Error("other network error");
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return fetch(url, {
 | 
					 | 
				
			||||||
        method: "GET",
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
            "Content-Type": "application/json;charset=utf-8",
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
        .then((response) => {
 | 
					    .catch(
 | 
				
			||||||
            if (response.ok) {
 | 
					      (
 | 
				
			||||||
                return response.json();
 | 
					        reason:
 | 
				
			||||||
            }
 | 
					          | NotFoundExceptionInterface
 | 
				
			||||||
 | 
					          | ServerExceptionInterface
 | 
				
			||||||
            if (response.status === 404) {
 | 
					          | ValidationExceptionInterface
 | 
				
			||||||
                throw NotFoundException(response);
 | 
					          | TransportExceptionInterface,
 | 
				
			||||||
            }
 | 
					      ) => {
 | 
				
			||||||
 | 
					        console.error(reason);
 | 
				
			||||||
            if (response.status === 422) {
 | 
					        throw reason;
 | 
				
			||||||
                return response.json().then((response) => {
 | 
					      },
 | 
				
			||||||
                    throw ValidationException(response);
 | 
					    );
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (response.status === 403) {
 | 
					 | 
				
			||||||
                throw AccessException(response);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (response.status >= 500) {
 | 
					 | 
				
			||||||
                return response.text().then((body) => {
 | 
					 | 
				
			||||||
                    throw ServerException(response.status, body);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            throw new Error("other network error");
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .catch(
 | 
					 | 
				
			||||||
            (
 | 
					 | 
				
			||||||
                reason:
 | 
					 | 
				
			||||||
                    | NotFoundExceptionInterface
 | 
					 | 
				
			||||||
                    | ServerExceptionInterface
 | 
					 | 
				
			||||||
                    | ValidationExceptionInterface
 | 
					 | 
				
			||||||
                    | TransportExceptionInterface,
 | 
					 | 
				
			||||||
            ) => {
 | 
					 | 
				
			||||||
                console.error(reason);
 | 
					 | 
				
			||||||
                throw reason;
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const fetchResults = async <T>(
 | 
					export const fetchResults = async <T>(
 | 
				
			||||||
    uri: string,
 | 
					  uri: string,
 | 
				
			||||||
    params?: FetchParams,
 | 
					  params?: FetchParams,
 | 
				
			||||||
): Promise<T[]> => {
 | 
					): Promise<T[]> => {
 | 
				
			||||||
    const promises: Promise<T[]>[] = [];
 | 
					  const promises: Promise<T[]>[] = [];
 | 
				
			||||||
    let page = 1;
 | 
					  let page = 1;
 | 
				
			||||||
    const firstData: PaginationResponse<T> = (await _fetchAction(
 | 
					  const firstData: PaginationResponse<T> = (await _fetchAction(
 | 
				
			||||||
        page,
 | 
					    page,
 | 
				
			||||||
        uri,
 | 
					    uri,
 | 
				
			||||||
        params,
 | 
					    params,
 | 
				
			||||||
    )) as PaginationResponse<T>;
 | 
					  )) as PaginationResponse<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    promises.push(Promise.resolve(firstData.results));
 | 
					  promises.push(Promise.resolve(firstData.results));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (firstData.pagination.more) {
 | 
					  if (firstData.pagination.more) {
 | 
				
			||||||
        do {
 | 
					    do {
 | 
				
			||||||
            page = ++page;
 | 
					      page = ++page;
 | 
				
			||||||
            promises.push(
 | 
					      promises.push(
 | 
				
			||||||
                _fetchAction<T>(page, uri, params).then((r) =>
 | 
					        _fetchAction<T>(page, uri, params).then((r) =>
 | 
				
			||||||
                    Promise.resolve(r.results),
 | 
					          Promise.resolve(r.results),
 | 
				
			||||||
                ),
 | 
					        ),
 | 
				
			||||||
            );
 | 
					      );
 | 
				
			||||||
        } while (page * firstData.pagination.items_per_page < firstData.count);
 | 
					    } while (page * firstData.pagination.items_per_page < firstData.count);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Promise.all(promises).then((values) => values.flat());
 | 
					  return Promise.all(promises).then((values) => values.flat());
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const fetchScopes = (): Promise<Scope[]> => {
 | 
					export const fetchScopes = (): Promise<Scope[]> => {
 | 
				
			||||||
    return fetchResults("/api/1.0/main/scope.json");
 | 
					  return fetchResults("/api/1.0/main/scope.json");
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Error objects to be thrown
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const ValidationException = (
 | 
					 | 
				
			||||||
    response: ValidationErrorResponse,
 | 
					 | 
				
			||||||
): ValidationExceptionInterface => {
 | 
					 | 
				
			||||||
    const error = {} as ValidationExceptionInterface;
 | 
					 | 
				
			||||||
    error.name = "ValidationException";
 | 
					 | 
				
			||||||
    error.violations = response.violations.map(
 | 
					 | 
				
			||||||
        (violation) => `${violation.title}: ${violation.propertyPath}`,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    error.titles = response.violations.map((violation) => violation.title);
 | 
					 | 
				
			||||||
    error.propertyPaths = response.violations.map(
 | 
					 | 
				
			||||||
        (violation) => violation.propertyPath,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    return error;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
const AccessException = (response: Response): AccessExceptionInterface => {
 | 
					const AccessException = (response: Response): AccessExceptionInterface => {
 | 
				
			||||||
    const error = {} as AccessExceptionInterface;
 | 
					  const error = {} as AccessExceptionInterface;
 | 
				
			||||||
    error.name = "AccessException";
 | 
					  error.name = "AccessException";
 | 
				
			||||||
    error.violations = ["You are not allowed to perform this action"];
 | 
					  error.violations = ["You are not allowed to perform this action"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return error;
 | 
					  return error;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					// eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
const NotFoundException = (response: Response): NotFoundExceptionInterface => {
 | 
					const NotFoundException = (response: Response): NotFoundExceptionInterface => {
 | 
				
			||||||
    const error = {} as NotFoundExceptionInterface;
 | 
					  const error = {} as NotFoundExceptionInterface;
 | 
				
			||||||
    error.name = "NotFoundException";
 | 
					  error.name = "NotFoundException";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return error;
 | 
					  return error;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ServerException = (
 | 
					const ServerException = (
 | 
				
			||||||
    code: number,
 | 
					  code: number,
 | 
				
			||||||
    body: string,
 | 
					  body: string,
 | 
				
			||||||
): ServerExceptionInterface => {
 | 
					): ServerExceptionInterface => {
 | 
				
			||||||
    const error = {} as ServerExceptionInterface;
 | 
					  const error = {} as ServerExceptionInterface;
 | 
				
			||||||
    error.name = "ServerException";
 | 
					  error.name = "ServerException";
 | 
				
			||||||
    error.code = code;
 | 
					  error.code = code;
 | 
				
			||||||
    error.body = body;
 | 
					  error.body = body;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return error;
 | 
					  return error;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ConflictHttpException = (
 | 
					const ConflictHttpException = (
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					  // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
    response: Response,
 | 
					  response: Response,
 | 
				
			||||||
): ConflictHttpExceptionInterface => {
 | 
					): ConflictHttpExceptionInterface => {
 | 
				
			||||||
    const error = {} as ConflictHttpExceptionInterface;
 | 
					  const error = {} as ConflictHttpExceptionInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    error.name = "ConflictHttpException";
 | 
					  error.name = "ConflictHttpException";
 | 
				
			||||||
    error.violations = [
 | 
					  error.violations = [
 | 
				
			||||||
        "Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again",
 | 
					    "Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again",
 | 
				
			||||||
    ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return error;
 | 
					  return error;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,17 +2,17 @@ import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
 | 
				
			|||||||
import { ExportGeneration } from "ChillMainAssets/types";
 | 
					import { ExportGeneration } from "ChillMainAssets/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const fetchExportGenerationStatus = async (
 | 
					export const fetchExportGenerationStatus = async (
 | 
				
			||||||
    exportGenerationId: string,
 | 
					  exportGenerationId: string,
 | 
				
			||||||
): Promise<ExportGeneration> =>
 | 
					): Promise<ExportGeneration> =>
 | 
				
			||||||
    makeFetch(
 | 
					  makeFetch(
 | 
				
			||||||
        "GET",
 | 
					    "GET",
 | 
				
			||||||
        `/api/1.0/main/export-generation/${exportGenerationId}/object`,
 | 
					    `/api/1.0/main/export-generation/${exportGenerationId}/object`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const generateFromSavedExport = async (
 | 
					export const generateFromSavedExport = async (
 | 
				
			||||||
    savedExportUuid: string,
 | 
					  savedExportUuid: string,
 | 
				
			||||||
): Promise<ExportGeneration> =>
 | 
					): Promise<ExportGeneration> =>
 | 
				
			||||||
    makeFetch(
 | 
					  makeFetch(
 | 
				
			||||||
        "POST",
 | 
					    "POST",
 | 
				
			||||||
        `/api/1.0/main/export/export-generation/create-from-saved-export/${savedExportUuid}`,
 | 
					    `/api/1.0/main/export/export-generation/create-from-saved-export/${savedExportUuid}`,
 | 
				
			||||||
    );
 | 
					  );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					import {Gender, GenderTranslation} from "ChillMainAssets/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Translates a given gender object into its corresponding gender translation string.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Gender|null} gender - The gender object to be translated, null values are also supported
 | 
				
			||||||
 | 
					 * @return {GenderTranslation} Returns the gender translation string corresponding to the provided gender,
 | 
				
			||||||
 | 
					 *                             or "unknown" if the gender is null.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function toGenderTranslation(gender: Gender|null): GenderTranslation
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (null === gender) {
 | 
				
			||||||
 | 
					    return "unknown";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return gender.genderTranslation;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@ import { fetchResults } from "./apiMethods";
 | 
				
			|||||||
import { Location, LocationType } from "../../types";
 | 
					import { Location, LocationType } from "../../types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getLocations = (): Promise<Location[]> =>
 | 
					export const getLocations = (): Promise<Location[]> =>
 | 
				
			||||||
    fetchResults("/api/1.0/main/location.json");
 | 
					  fetchResults("/api/1.0/main/location.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getLocationTypes = (): Promise<LocationType[]> =>
 | 
					export const getLocationTypes = (): Promise<LocationType[]> =>
 | 
				
			||||||
    fetchResults("/api/1.0/main/location-type.json");
 | 
					  fetchResults("/api/1.0/main/location-type.json");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,3 @@
 | 
				
			|||||||
export function buildReturnPath(location: Location): string {
 | 
					export function buildReturnPath(location: Location): string {
 | 
				
			||||||
    return location.pathname + location.search;
 | 
					  return location.pathname + location.search;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,23 +2,23 @@ import { User } from "../../types";
 | 
				
			|||||||
import { makeFetch } from "./apiMethods";
 | 
					import { makeFetch } from "./apiMethods";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const whoami = (): Promise<User> => {
 | 
					export const whoami = (): Promise<User> => {
 | 
				
			||||||
    const url = `/api/1.0/main/whoami.json`;
 | 
					  const url = `/api/1.0/main/whoami.json`;
 | 
				
			||||||
    return fetch(url).then((response) => {
 | 
					  return fetch(url).then((response) => {
 | 
				
			||||||
        if (response.ok) {
 | 
					    if (response.ok) {
 | 
				
			||||||
            return response.json();
 | 
					      return response.json();
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
        throw {
 | 
					    throw {
 | 
				
			||||||
            msg: "Error while getting whoami.",
 | 
					      msg: "Error while getting whoami.",
 | 
				
			||||||
            sta: response.status,
 | 
					      sta: response.status,
 | 
				
			||||||
            txt: response.statusText,
 | 
					      txt: response.statusText,
 | 
				
			||||||
            err: new Error(),
 | 
					      err: new Error(),
 | 
				
			||||||
            body: response.body,
 | 
					      body: response.body,
 | 
				
			||||||
        };
 | 
					    };
 | 
				
			||||||
    });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const whereami = (): Promise<Location | null> => {
 | 
					export const whereami = (): Promise<Location | null> => {
 | 
				
			||||||
    const url = `/api/1.0/main/user-current-location.json`;
 | 
					  const url = `/api/1.0/main/user-current-location.json`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return makeFetch<null, Location | null>("GET", url);
 | 
					  return makeFetch<null, Location | null>("GET", url);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,22 @@
 | 
				
			|||||||
const buildLinkCreate = function (
 | 
					const buildLinkCreate = function (
 | 
				
			||||||
    relatedEntityClass: string,
 | 
					  relatedEntityClass: string,
 | 
				
			||||||
    relatedEntityId: number,
 | 
					  relatedEntityId: number,
 | 
				
			||||||
    to: number | null,
 | 
					  to: number | null,
 | 
				
			||||||
    returnPath: string | null,
 | 
					  returnPath: string | null,
 | 
				
			||||||
): string {
 | 
					): string {
 | 
				
			||||||
    const params = new URLSearchParams();
 | 
					  const params = new URLSearchParams();
 | 
				
			||||||
    params.append("entityClass", relatedEntityClass);
 | 
					  params.append("entityClass", relatedEntityClass);
 | 
				
			||||||
    params.append("entityId", relatedEntityId.toString());
 | 
					  params.append("entityId", relatedEntityId.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null !== to) {
 | 
					  if (null !== to) {
 | 
				
			||||||
        params.append("tos[0]", to.toString());
 | 
					    params.append("tos[0]", to.toString());
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (null !== returnPath) {
 | 
					  if (null !== returnPath) {
 | 
				
			||||||
        params.append("returnPath", returnPath);
 | 
					    params.append("returnPath", returnPath);
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `/fr/notification/create?${params.toString()}`;
 | 
					  return `/fr/notification/create?${params.toString()}`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { buildLinkCreate };
 | 
					export { buildLinkCreate };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,17 +10,17 @@
 | 
				
			|||||||
 * @throws {Error} If the related entity ID is undefined.
 | 
					 * @throws {Error} If the related entity ID is undefined.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const buildLinkCreate = (
 | 
					export const buildLinkCreate = (
 | 
				
			||||||
    workflowName: string,
 | 
					  workflowName: string,
 | 
				
			||||||
    relatedEntityClass: string,
 | 
					  relatedEntityClass: string,
 | 
				
			||||||
    relatedEntityId: number | undefined,
 | 
					  relatedEntityId: number | undefined,
 | 
				
			||||||
): string => {
 | 
					): string => {
 | 
				
			||||||
    if (typeof relatedEntityId === "undefined") {
 | 
					  if (typeof relatedEntityId === "undefined") {
 | 
				
			||||||
        throw new Error("the related entity id is not set");
 | 
					    throw new Error("the related entity id is not set");
 | 
				
			||||||
    }
 | 
					  }
 | 
				
			||||||
    const params = new URLSearchParams();
 | 
					  const params = new URLSearchParams();
 | 
				
			||||||
    params.set("entityClass", relatedEntityClass);
 | 
					  params.set("entityClass", relatedEntityClass);
 | 
				
			||||||
    params.set("entityId", relatedEntityId.toString(10));
 | 
					  params.set("entityId", relatedEntityId.toString(10));
 | 
				
			||||||
    params.set("workflow", workflowName);
 | 
					  params.set("workflow", workflowName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `/fr/main/workflow/create?` + params.toString();
 | 
					  return `/fr/main/workflow/create?` + params.toString();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { TranslatableString } from "ChillMainAssets/types";
 | 
					import { DateTime, TranslatableString } from "ChillMainAssets/types";
 | 
				
			||||||
 | 
					import { getLocale } from "translator";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Localizes a translatable string object based on the current locale.
 | 
					 * Localizes a translatable string object based on the current locale.
 | 
				
			||||||
@@ -8,34 +9,97 @@ import { TranslatableString } from "ChillMainAssets/types";
 | 
				
			|||||||
 * @param locale defaults to browser locale
 | 
					 * @param locale defaults to browser locale
 | 
				
			||||||
 * @returns The localized string or null if no translation is available
 | 
					 * @returns The localized string or null if no translation is available
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function localizeString(
 | 
					 | 
				
			||||||
    translatableString: TranslatableString | null | undefined,
 | 
					 | 
				
			||||||
    locale?: string,
 | 
					 | 
				
			||||||
): string {
 | 
					 | 
				
			||||||
    if (!translatableString || Object.keys(translatableString).length === 0) {
 | 
					 | 
				
			||||||
        return "";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const currentLocale = locale || navigator.language.split("-")[0] || "fr";
 | 
					/**
 | 
				
			||||||
 | 
					 * Prepends the current HTML lang code to the given URL.
 | 
				
			||||||
    if (translatableString[currentLocale]) {
 | 
					 * Example: If lang="fr" and url="/about", returns "/fr/about"
 | 
				
			||||||
        return translatableString[currentLocale];
 | 
					 *
 | 
				
			||||||
    }
 | 
					 * @param url The URL to localize
 | 
				
			||||||
 | 
					 * @returns The localized URL
 | 
				
			||||||
    // Define fallback locales
 | 
					 */
 | 
				
			||||||
    const fallbackLocales: string[] = ["fr", "en"];
 | 
					export function localizedUrl(url: string): string {
 | 
				
			||||||
 | 
					  const locale = getLocale();
 | 
				
			||||||
    for (const fallbackLocale of fallbackLocales) {
 | 
					  // Ensure url starts with a slash and does not already start with /{lang}/
 | 
				
			||||||
        if (translatableString[fallbackLocale]) {
 | 
					  const normalizedUrl = url.startsWith("/") ? url : `/${url}`;
 | 
				
			||||||
            return translatableString[fallbackLocale];
 | 
					  const langPrefix = `/${locale}`;
 | 
				
			||||||
        }
 | 
					  if (normalizedUrl.startsWith(langPrefix + "/")) {
 | 
				
			||||||
    }
 | 
					    return normalizedUrl;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
    // No fallback translation found, use the first available
 | 
					  return `${langPrefix}${normalizedUrl}`;
 | 
				
			||||||
    const availableLocales = Object.keys(translatableString);
 | 
					 | 
				
			||||||
    if (availableLocales.length > 0) {
 | 
					 | 
				
			||||||
        return translatableString[availableLocales[0]];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return "";
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function localizeString(
 | 
				
			||||||
 | 
					  translatableString: TranslatableString | null | undefined,
 | 
				
			||||||
 | 
					  locale?: string,
 | 
				
			||||||
 | 
					): string {
 | 
				
			||||||
 | 
					  if (!translatableString || Object.keys(translatableString).length === 0) {
 | 
				
			||||||
 | 
					    return "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const currentLocale = locale || getLocale();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (translatableString[currentLocale]) {
 | 
				
			||||||
 | 
					    return translatableString[currentLocale];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Define fallback locales
 | 
				
			||||||
 | 
					  const fallbackLocales: string[] = ["fr", "en"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const fallbackLocale of fallbackLocales) {
 | 
				
			||||||
 | 
					    if (translatableString[fallbackLocale]) {
 | 
				
			||||||
 | 
					      return translatableString[fallbackLocale];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // No fallback translation found, use the first available
 | 
				
			||||||
 | 
					  const availableLocales = Object.keys(translatableString);
 | 
				
			||||||
 | 
					  if (availableLocales.length > 0) {
 | 
				
			||||||
 | 
					    return translatableString[availableLocales[0]];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return "";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const datetimeFormats: Record<
 | 
				
			||||||
 | 
					  string,
 | 
				
			||||||
 | 
					  Record<string, Intl.DateTimeFormatOptions>
 | 
				
			||||||
 | 
					> = {
 | 
				
			||||||
 | 
					  fr: {
 | 
				
			||||||
 | 
					    short: {
 | 
				
			||||||
 | 
					      year: "numeric",
 | 
				
			||||||
 | 
					      month: "numeric",
 | 
				
			||||||
 | 
					      day: "numeric",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    text: {
 | 
				
			||||||
 | 
					      year: "numeric",
 | 
				
			||||||
 | 
					      month: "long",
 | 
				
			||||||
 | 
					      day: "numeric",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    long: {
 | 
				
			||||||
 | 
					      year: "numeric",
 | 
				
			||||||
 | 
					      month: "numeric",
 | 
				
			||||||
 | 
					      day: "numeric",
 | 
				
			||||||
 | 
					      hour: "numeric",
 | 
				
			||||||
 | 
					      minute: "numeric",
 | 
				
			||||||
 | 
					      hour12: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    hoursOnly: {
 | 
				
			||||||
 | 
					      hour: "numeric",
 | 
				
			||||||
 | 
					      minute: "numeric",
 | 
				
			||||||
 | 
					      hour12: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export function localizeDateTimeFormat(
 | 
				
			||||||
 | 
					  dateTime: DateTime,
 | 
				
			||||||
 | 
					  format: keyof typeof datetimeFormats.fr = "short",
 | 
				
			||||||
 | 
					): string {
 | 
				
			||||||
 | 
					  const locale = getLocale();
 | 
				
			||||||
 | 
					  const options =
 | 
				
			||||||
 | 
					    datetimeFormats[locale]?.[format] || datetimeFormats.fr[format];
 | 
				
			||||||
 | 
					  return new Intl.DateTimeFormat(locale, options).format(
 | 
				
			||||||
 | 
					    new Date(dateTime.datetime),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default datetimeFormats;
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user